How do I pass build number from Nant back to Cruise Control - version-control

I have a Nant build script which CruiseControl uses to build a solution on-demand.
However, we only recently got CruiseControl so our official build number is different from what is listed in CruiseControl.
I know CruiseControl injects some properties into build scripts so that I can access the CC build number in the script (CCNetLabel) but how do I pass a value back to CC to use as the build number on the UI screen?
Example, CC says build number 2
nAnt script increments a buildnumber.xml value every build, and the official build number is on 123.
I want the CC UI to show last successful build number: 123, not 2, so how do I pass that value back up?

A custom build labeler is required for this. Perforce is our source control provider and we derive our version number from it. The code is as follows:
/// <summary>
/// Gets the latest change list number from perforce, for ccnet to consume as a build label.
/// </summary>
[ReflectorType( "p4labeller" )]
public class PerforceLabeller : ILabeller
{
// perforce executable (optional)
[ReflectorProperty("executable", Required = false)]
public string P4Executable = "p4.exe";
// perforce port (i.e. myserver:1234)
[ReflectorProperty("port", Required = false)]
public string P4Port = String.Empty;
// perforce user
[ReflectorProperty("user", Required = false)]
public string P4User = String.Empty;
// perforce client
[ReflectorProperty("client", Required = false)]
public string P4Client = String.Empty;
// perforce view (i.e. //Dev/Code1/...)
[ReflectorProperty("view", Required = false)]
public string P4View = String.Empty;
// Returns latest change list
public string Generate( IIntegrationResult previousLabel )
{
return GetLatestChangelist();
}
// Stores latest change list into a label
public void Run( IIntegrationResult result )
{
result.Label = GetLatestChangelist();
}
// Gets the latest change list
public string GetLatestChangelist()
{
// Build the arguments to pass to p4 to get the latest changelist
string theArgs = "-p " + P4Port + " -u " + P4User + " -c " + P4Client + " changes -m 1 -s submitted " + P4View;
Log.Info( string.Format( "Getting latest change from Perforce using --> " + theArgs ) );
// Execute p4
ProcessResult theProcessResult = new ProcessExecutor().Execute( new ProcessInfo( P4Executable, theArgs ) );
// Extract the changelist # from the result
Regex theRegex = new Regex( #"\s[0-9]+\s", RegexOptions.IgnoreCase );
Match theMatch = theRegex.Match( theProcessResult.StandardOutput );
return theMatch.Value.Trim();
}
}
The method, GetLatestChangelist, is where you would probably insert your own logic to talk to your version control system. In Perforce there is the idea of the last changelist which is unique. Our build numbers, and ultimately version numbers are based off of that.
Once you build this (into an assembly dll), you'll have to hook it into ccnet. You can just drop the assembly into the server directory (next to ccnet.exe).
Next you modify your ccnet project file to utilize this labeller. We did this with the default labeller block. Something like the following:
<project>
<labeller type="p4labeller">
<client>myclient</client>
<executable>p4.exe</executable>
<port>myserver:1234</port>
<user>myuser</user>
<view>//Code1/...</view>
</labeller>
<!-- Other project configuration to go here -->
</project>
If you're just wanting the build number to show up in ccnet then you're done and don't really need to do anything else. However, you can access the label in your NAnt script if you wish by using the already provided CCNetLabel property.
Hope this helps some. Let me know if you have any questions by posting to the comments.

Did you try to use some environment variables? I believe CCNet can handle these.
I'll dig a bit on this.
Well I see a solution, quite dirty, but anyhow:
1- Add a defaultlabeller section in your CCNET project definition. It will contains the pattern of the build number you want to display.
2- Within NAnt, have a script to update your configuration file, inserting the build number you want to see.
3- Touch (in the Unix sense) the ccnet.exe.config file so as to make it re-load the projects configuration files.
et voilà.

We had this problem as well. I ended up writing a special CC labelling plugin.

If your build numbers are sequential, you can just hack the cruise control state file to give it the correct build number to start with. Your looking for a file called [projectName].state.
I changed the Label element to the correct number and the LastSuccessfulIntegrationLabel to be the new number.

However, we only recently got
CruiseControl so our official build
number is different from what is
listed in CruiseControl.
Sort of along the lines of what gbanfill said, you can tell CC what build numbers to start from, but there's no need to hack the .ser file. You can use the JMX interface to set the current build number to get it in sync with your NAnt build number.
You can also set the default label value to to your current build number, delete the .ser file and restart CC.
But maybe the easiest thing is to write the build number into a property file from NAnt and then use the property file label incrementer to read that file. (Be sure to to set setPreBuildIncrementer="true")

Related

Bukkit - Why is it displaying null (using a config file)

So, I am making a custom plugin for my server, and one of my features requires me to set an integer in a gui that shows how many 'CommonPackages' a user has. The issue that I am having is that when I am getting the String from my config (My config uses a custom file creation/management class that was given to me by a friend) it is saying that it is null in the gui, I do not get any errors in the console, please may someone help me? The item in the gui and the code for setting the item in the gui.
Item in the gui
gui creation code:
public static Inventory WhiteBackpack(Player player) {
UUID uuid = player.getUniqueId();
Inventory inv = Bukkit.createInventory(null, 27, (inventoryname));
ItemStack common = new ItemStack(Material.INK_SACK);
common.setDurability((byte) 8);
ItemMeta commonMeta = common.getItemMeta();
commonMeta.setDisplayName(Utils.chat("&fCommon Packages &8» &f&l" + Main.pl.getFileControl().getConfig().getString("players." + uuid + ".Packages.Common"))); //How I access my custom configs.
common.setItemMeta(commonMeta);
inv.setItem(10, common);
return inv;
}
Without the code of your method to get the config I can only say that the string in the actual file is not present.
As the Bukkit documentation states:
If the String does not exist and no default value was specified, this will return null.
So either the key just does not exist in the file or you pointed to the wrong file. The configuration should be well formated, too. (no tabs, only spaces)

Changing working directory in Scala [duplicate]

How can I change the current working directory from within a Java program? Everything I've been able to find about the issue claims that you simply can't do it, but I can't believe that that's really the case.
I have a piece of code that opens a file using a hard-coded relative file path from the directory it's normally started in, and I just want to be able to use that code from within a different Java program without having to start it from within a particular directory. It seems like you should just be able to call System.setProperty( "user.dir", "/path/to/dir" ), but as far as I can figure out, calling that line just silently fails and does nothing.
I would understand if Java didn't allow you to do this, if it weren't for the fact that it allows you to get the current working directory, and even allows you to open files using relative file paths....
There is no reliable way to do this in pure Java. Setting the user.dir property via System.setProperty() or java -Duser.dir=... does seem to affect subsequent creations of Files, but not e.g. FileOutputStreams.
The File(String parent, String child) constructor can help if you build up your directory path separately from your file path, allowing easier swapping.
An alternative is to set up a script to run Java from a different directory, or use JNI native code as suggested below.
The relevant OpenJDK bug was closed in 2008 as "will not fix".
If you run your legacy program with ProcessBuilder, you will be able to specify its working directory.
There is a way to do this using the system property "user.dir". The key part to understand is that getAbsoluteFile() must be called (as shown below) or else relative paths will be resolved against the default "user.dir" value.
import java.io.*;
public class FileUtils
{
public static boolean setCurrentDirectory(String directory_name)
{
boolean result = false; // Boolean indicating whether directory was set
File directory; // Desired current working directory
directory = new File(directory_name).getAbsoluteFile();
if (directory.exists() || directory.mkdirs())
{
result = (System.setProperty("user.dir", directory.getAbsolutePath()) != null);
}
return result;
}
public static PrintWriter openOutputFile(String file_name)
{
PrintWriter output = null; // File to open for writing
try
{
output = new PrintWriter(new File(file_name).getAbsoluteFile());
}
catch (Exception exception) {}
return output;
}
public static void main(String[] args) throws Exception
{
FileUtils.openOutputFile("DefaultDirectoryFile.txt");
FileUtils.setCurrentDirectory("NewCurrentDirectory");
FileUtils.openOutputFile("CurrentDirectoryFile.txt");
}
}
It is possible to change the PWD, using JNA/JNI to make calls to libc. The JRuby guys have a handy java library for making POSIX calls called jnr-posix. Here's the maven info
As mentioned you can't change the CWD of the JVM but if you were to launch another process using Runtime.exec() you can use the overloaded method that lets you specify the working directory. This is not really for running your Java program in another directory but for many cases when one needs to launch another program like a Perl script for example, you can specify the working directory of that script while leaving the working dir of the JVM unchanged.
See Runtime.exec javadocs
Specifically,
public Process exec(String[] cmdarray,String[] envp, File dir) throws IOException
where dir is the working directory to run the subprocess in
If I understand correctly, a Java program starts with a copy of the current environment variables. Any changes via System.setProperty(String, String) are modifying the copy, not the original environment variables. Not that this provides a thorough reason as to why Sun chose this behavior, but perhaps it sheds a little light...
The working directory is a operating system feature (set when the process starts).
Why don't you just pass your own System property (-Dsomeprop=/my/path) and use that in your code as the parent of your File:
File f = new File ( System.getProperty("someprop"), myFilename)
The smarter/easier thing to do here is to just change your code so that instead of opening the file assuming that it exists in the current working directory (I assume you are doing something like new File("blah.txt"), just build the path to the file yourself.
Let the user pass in the base directory, read it from a config file, fall back to user.dir if the other properties can't be found, etc. But it's a whole lot easier to improve the logic in your program than it is to change how environment variables work.
I have tried to invoke
String oldDir = System.setProperty("user.dir", currdir.getAbsolutePath());
It seems to work. But
File myFile = new File("localpath.ext");
InputStream openit = new FileInputStream(myFile);
throws a FileNotFoundException though
myFile.getAbsolutePath()
shows the correct path.
I have read this. I think the problem is:
Java knows the current directory with the new setting.
But the file handling is done by the operation system. It does not know the new set current directory, unfortunately.
The solution may be:
File myFile = new File(System.getPropety("user.dir"), "localpath.ext");
It creates a file Object as absolute one with the current directory which is known by the JVM. But that code should be existing in a used class, it needs changing of reused codes.
~~~~JcHartmut
You can use
new File("relative/path").getAbsoluteFile()
after
System.setProperty("user.dir", "/some/directory")
System.setProperty("user.dir", "C:/OtherProject");
File file = new File("data/data.csv").getAbsoluteFile();
System.out.println(file.getPath());
Will print
C:\OtherProject\data\data.csv
You can change the process's actual working directory using JNI or JNA.
With JNI, you can use native functions to set the directory. The POSIX method is chdir(). On Windows, you can use SetCurrentDirectory().
With JNA, you can wrap the native functions in Java binders.
For Windows:
private static interface MyKernel32 extends Library {
public MyKernel32 INSTANCE = (MyKernel32) Native.loadLibrary("Kernel32", MyKernel32.class);
/** BOOL SetCurrentDirectory( LPCTSTR lpPathName ); */
int SetCurrentDirectoryW(char[] pathName);
}
For POSIX systems:
private interface MyCLibrary extends Library {
MyCLibrary INSTANCE = (MyCLibrary) Native.loadLibrary("c", MyCLibrary.class);
/** int chdir(const char *path); */
int chdir( String path );
}
The other possible answer to this question may depend on the reason you are opening the file. Is this a property file or a file that has some configuration related to your application?
If this is the case you may consider trying to load the file through the classpath loader, this way you can load any file Java has access to.
If you run your commands in a shell you can write something like "java -cp" and add any directories you want separated by ":" if java doesnt find something in one directory it will go try and find them in the other directories, that is what I do.
Use FileSystemView
private FileSystemView fileSystemView;
fileSystemView = FileSystemView.getFileSystemView();
currentDirectory = new File(".");
//listing currentDirectory
File[] filesAndDirs = fileSystemView.getFiles(currentDirectory, false);
fileList = new ArrayList<File>();
dirList = new ArrayList<File>();
for (File file : filesAndDirs) {
if (file.isDirectory())
dirList.add(file);
else
fileList.add(file);
}
Collections.sort(dirList);
if (!fileSystemView.isFileSystemRoot(currentDirectory))
dirList.add(0, new File(".."));
Collections.sort(fileList);
//change
currentDirectory = fileSystemView.getParentDirectory(currentDirectory);

Change name of generated Context file with EF PowerTools Reverse Engineer Code First

I have been attempting to figure out how to make the EF Power Tools - Reverse Engineer Code First use a different name for the generated Context-file, than what it uses now.
Example
I have a database called My_Awesome_Dev_Database. When I run Reverse-engineer against that, the file that is generated will be called:
My_Awesome_Dev_DatabaseContext.cs
What it would like to do is specify what the file is to be called, for instance:
MyAwesomeDatabaseContext.cs
Attempts so far
I have tried looking through the EF.Utilities.CS.ttinclude file, to figure out how the filename is generated - but I have been unsuccessful so far.
Does anyone know ?
Thanks in advance!
Currently the generated context file naming convention is hard-coded and non configurable.
All the logic is inside the ReverseEngineerCodeFirstHandler class (the source is on CodePlex).
It sets the context file name and path with
var contextFilePath = Path.Combine(modelsDirectory,
modelGenerator.EntityContainer.Name + contextHost.FileExtension);
var contextItem = project.AddNewFile(contextFilePath, contextContents);
So the file name is coming from modelGenerator.EntityContainer.Name which gets created upper in the method with:
var contextName =
connection.Database.Replace(" ", string.Empty)
.Replace(".", string.Empty) + "Context";
var modelGenerator =
new EntityModelSchemaGenerator(storeGenerator.EntityContainer,
"DefaultNamespace", contextName);
So as you can see the tool just takes the db name removes the spaces and dots and use it as the context name which will end up as the generated file name.
You can open an issue or - because Entity Framework is open source - take the code, add this configuration option, and send back a pull request.

install4j: how can i pass command line arguments to windows service

I've created a windows service using install4j and everything works but now I need to pass it command line arguments to the service. I know I can configure them at service creation time in the new service wizard but i was hoping to either pass the arguments to the register service command ie:
myservice.exe --install --arg arg1=val1 --arg arg1=val2 "My Service Name1"
or by putting them in the .vmoptions file like:
-Xmx256m
arg1=val1
arg2=val2
It seems like the only way to do this is to modify my code to pick up the service name via exe4j.launchName and then load some other file or environment variables that has the necessary configuration for that particular service. I've used other service creation tools for java in the past and they all had straightforward support for command line arguments registered by the user.
I know you asked this back in January, but did you ever figure this out?
I don't know where you're sourcing val1, val2 etc from. Are they entered by the user into fields in a form in the installation process? Assuming they are, then this is a similar problem to one I faced a while back.
My approach for this was to have a Configurable Form with the necessary fields (as Text Field objects), and obviously have variables assigned to the values of the text fields (under the 'User Input/Variable Name' category of the text field).
Later in the installation process I had a Display Progress screen with a Run Script action attached to it with some java to achieve what I wanted to do.
There are two 'gotchas' when optionally setting variables in install4j this way. Firstly, the variable HAS to be set no matter what, even if it's just to the empty string. So, if the user leaves a field blank (ie they don't want to pass that argument into the service), you'll still need to provide an empty string to the Run executable or Launch Service task (more in that in a moment) Secondly, arguments can't have spaces - every space-separated argument has to have its own line.
With that in mind, here's a Run script code snippet that might achieve what you want:
final String[] argumentNames = {"arg1", "arg2", "arg3"};
// For each argument this method creates two variables. For example for arg1 it creates
// arg1ArgumentIdentifierOptional and arg1ArgumentAssignmentOptional.
// If the value of the variable set from the previous form (in this case, arg1) is not empty, then it will
// set 'arg1ArgumentIdentifierOptional' to '--arg', and 'arg1ArgumentAssignmentOptional' to the string arg1=val1 (where val1
// was the value the user entered in the form for the variable).
// Otherwise, both arg1ArgumentIdentifierOptional and arg1ArgumentAssignmentOptional will be set to empty.
//
// This allows the installer to pass both parameters in a later Run executable task without worrying about if they're
// set or not.
for (String argumentName : argumentNames) {
String argumentValue = context.getVariable(argumentName)==null?null:context.getVariable(argumentName)+"";
boolean valueNonEmpty = (argumentValue != null && argumentValue.length() > 0);
context.setVariable(
argumentName + "ArgumentIdentifierOptional",
valueNonEmpty ? "--arg": ""
);
context.setVariable(
argumentName + "ArgumentAssignmentOptional",
valueNonEmpty ? argumentName+"="+argumentValue : ""
);
}
return true;
The final step is to launch the service or executable. I'm not too sure how services work, but with the executable, you create the task then edit the 'Arguments' field, giving it a line-separated list of values.
So in your case, it might look like this:
--install
${installer:arg1ArgumentIdentifierOptional}
${installer:arg1ArgumentAssignmentOptional}
${installer:arg2ArgumentIdentifierOptional}
${installer:arg2ArgumentAssignmentOptional}
${installer:arg3ArgumentIdentifierOptional}
${installer:arg3ArgumentAssignmentOptional}
"My Service Name1"
And that's it. If anyone else knows how to do this better feel free to improve on this method (this is for install4j 4.2.8, btw).

How can I Diff a Svn Repository using SharpSvn

My question is quite simple and with the SharpSvn Api, it should be easy as well. Here what I did:
path = "c:\project";
using (SvnLookClient client = new SvnLookClient())
{
SvnLookOrigin o = new SvnLookOrigin(path);
Collection<SvnChangedEventArgs> changeList;
client.GetChanged(o, out changeList); // <-- Exception
}
and when I call the GetChanged, I get an exception:
Can't open file 'c:\project\format': The system cannot find the file specified.
So, Maybe there is something I'm missing? Or maybe it's not the right way to do find out the list of files and folders that were modified in the local repository?
Thanks in advance.
The SvnLookClient class in SharpSvn is the equivalent to the 'svnlook' console application. It is a low level tool that enables repository hooks to look into specific transactions of a repository using direct file access.
You probably want to use the SvnClient class to look at a WorkingCopy and most likely its Status() or in some cases simpler GetStatus() function to see what changed.
The path that the SvnLookOrigin constructor wants is actually:
path = "c:\project\.svn\";
That is, it wants that special ".svn" directory not just the root of where the source is checked out to.
Although you probably do want to listen to Bert and do something like:
path = "c:\project";
using (SvnLookClient client = new SvnLookClient())
{
SvnLookOrigin o = new SvnLookOrigin(path);
Collection<SvnChangedEventArgs> changeList;
client.GetStatus(o, out changeList); // Should now return the differences between this working copy and the remote status.
}