I'm having trouble getting this to work and hope that one of you have done this before in .NET. The code I have seems like it should work, but unfortunately I can't directly test it because it's being executed by a subversion hook script (post-commit.bat)... Essentially, my program is like a wrapper for svn export. I'm thinking that what's happening is svn is prompting for confirmation to accept the SSL certificate, but since it's an automated process there is no user to respond. As a result, I'm attempting to simulate the user's response by writing to svn's stdin stream.
I'm starting svn with a System.Diagnostics.ProcessStartInfo instance and System.Diagnostics.Process.Start() as follows...
1 | using System; |
2 | using System.Diagnostics; |
3 | using System.IO; |
4 | |
5 | . |
6 | . |
7 | . |
8 | |
9 | int svn_timeout = (int)Math.Round(TimeSpan.FromMinutes(1).TotalMilliseconds, 0); |
10 | |
11 | Process svn = null; |
12 | ProcessStartInfo svn_startinfo = new ProcessStartInfo("svn", "export ..."); |
13 | |
14 | psi.RedirectStandardError = true; |
15 | psi.RedirectStandardInput = true; |
16 | psi.RedirectStandardOutput = true; |
17 | psi.WorkingDirectory = Environment.CurrentDirectory; |
18 | psi.UseShellExecute = false; |
19 | |
20 | // Try to start svn. |
21 | try |
22 | { |
23 | // Start svn. |
24 | svn = Process.Start(svn_startinfo); |
25 | } |
26 | catch(Exception) |
27 | { |
28 | // Print/log error and terminate with -1 exit status. |
29 | } |
30 | |
31 | // Wait for svn to exit. |
32 | if(!svn.WaitForExit(svn_timeout)) |
33 | { |
34 | // Print/log error and terminate with -1 exit status. |
35 | } |
36 | |
37 | // Check svn exit status. |
38 | if(svn.ExitCode != 0) |
39 | { |
40 | // Print/log error and terminate with -1 exit status. |
41 | } |
42 | |
43 | // Print/log success. |
I think it should work to WriteLine() a response to the StandardInput StreamWriter of the svn process.
1 | // |<--- svn = Process.Start() |
2 | |
3 | /* |
4 | * I'd like to change this to only write if svn prompts. That should probably |
5 | * involve reading from svn.StandardOutput and looking for a particular message, |
6 | * which I don't currently have because my system already accepted the certificate. |
7 | * There is a WaitForInputIdle() method of the Process class, but the summary |
8 | * suggests that it is for GUI applications only. |
9 | */ |
10 | try |
11 | { |
12 | // (t)emporarily accept certificate. |
13 | svn.StandardInput.WriteLine("t"); |
14 | |
15 | // Flush buffer to stream. |
16 | svn.StandardInput.Flush(); |
17 | } |
18 | catch(Exception) |
19 | { |
20 | // Print/log error and terminate with -1 exit status. |
21 | } |
22 | |
23 | // svn.WaitForExit() --->| |
I've added log messages before and after the above code where I write to the stdin stream. Both are logged, but nothing more. If svn fails to start, times out, or exits with a non-zero exit status that should be logged. If svn succeeds that should be logged. Instead, I get nothing to stderr, stdout, and nothing more to my log file. And my process just seems to hang there doing nothing.
I wonder if maybe the way I'm launching svn is wrong. Useful posts on the matter seem few and far between, but I did find one that suggested that the application being written to was terminating itself before the write could happen... Maybe something about the way I'm starting svn tells it to terminate if it starts to wait...
Does anybody have experience with this in .NET?
I'm thinking that what's happening is svn is prompting for confirmation to accept the SSL certificate, but since it's an automated process there is no user to respond. As a result, I'm attempting to simulate the user's response by writing to svn's stdin stream.
If this really is the case, then the better solution is to permanently accept the certificate.
That should probably involve reading from svn.StandardOutput and looking for a particular message, which I don't currently have because my system already accepted the certificate.
Remove your %APPDATA%\Subversion directory.
Your best best would be to log on as the user that the subversion server is running as, permanently accept the certificate, and be done with it.
[append]
After experimentation, it seems that the certificate is stored in %APPDATA%\Subversion\auth\svn.ssl.server\<hash>. Using that information, you should be able to copy the certificate into the target configuration.
If this really is the case, then the better solution is to permanently accept the certificate.
...
Your best best would be to log on as the user that the subversion server is running as, permanently accept the certificate, and be done with it.
I agree, that is an excellent solution, but unfortunately I hit another snag there. I don't know why, but my Subversion hook (which is executing my program, which then executes svn) is being executed as a "phantom" user (and I just made that up so it has no specific meaning). The USERNAME environment variable is apparently an empty string. I haven't figured that out yet, since every workstation I've tested (as opposed to the development server) has an actual user executing it... So it's not going to be quite so easy, but I would certainly like to figure out why the USERNAME variable is empty...
Does anybody know where the Subversion hook scripts get their user credentials from?
After experimentation, it seems that the certificate is stored in %APPDATA%\Subversion\auth\svn.ssl.server\<hash>. Using that information, you should be able to copy the certificate into the target configuration.
That sounds like a nice solution, but unfortunately the APPDATA environment variable is also empty when the hook script is executed. So I don't think Subversion is creating any kind of user data...
So it's running as NETWORK SERVICE or NETWORK GUEST or some such... Perhaps you can create the configuration in All Users\Subversion and then set the environment variable yourself? Sounds easier.
Oh, and subversion hook scripts run under the subversion server process. If that's IIS, then it's going to be the NETWORK SERVICE or NETWORK GUEST accounts.
We're running VisualSVN Server, which AFAIK is using Apache. Personally, I wanted to just install Subversion and Apache myself...
Here's one:
svn export --config-dir=C:\svnconfig ...
Got that out of svn help export. Should fix you up, right?
Also, there's a --non-interactive option, but that will probably cause it to fail silently.
I was looking at that, but I wasn't sure the --config-dir is the %APPDATA%\Subversion equivalent... I tried searching for confirmation, but couldn't find it... In hindsight I suppose it should be obvious, but testing is difficult and abstract through a hook script... Plus I've been pretty tired these past few days.
So, does it work?
I can't say... I'm no longer at work and I'm honestly too tired to test it right now...
Yeah, so I'm going to keep bumping this thread so when it doesn't work you can just reply instead of starting a new thread
Heh, I'm now at work... Now I need to figure out how to go about testing this...
** EDIT **
It's taking longer than expected. Was having problems getting the service process to run as a custom user with restricted access and ownership of the desired directories. Hopefully I can confirm whether this --config-dir option solves my problem in the next hour.
I'm still curious why writing to stdin didn't work though... It was a poor solution to this problem, but it would be nice to understand how it works to write to stdin of another process like that for future reference.
** EDIT **
It just occurred to me to just try svn log on my local machine with a new --config-dir option specified to see if it would create the directory and prompt me again for authentication and the SSL certificate. It did so I'm pretty confident that this solution will work... At least, if nothing else goes wrong.
I'm still curious why writing to stdin didn't work though... It was a poor solution to this problem, but it would be nice to understand how it works to write to stdin of another process like that for future reference.
On Linux, password prompts do not read from stdin: instead, they read from the owner terminal. This means you can't echo password | ssh host. Something similar may be going on for subversion.
I'm unsure if the config directory is working or not... svn appears to be still prompting for confirmation, which doesn't seem to make sense because if a human user executes an svn command while pointing to the created Subversion client config directory they are not prompted to accept the certificate...
On Linux, password prompts do not read from stdin: instead, they read from the owner terminal. This means you can't echo password | ssh host. Something similar may be going on for subversion.
Well I wasn't actually writing the password to svn's stdin. Instead, I was writing a 't' character to temporarily accept the SSL certificate, which I just confirmed works from the command line in Windows by piping it.
svn log url
<svn prompt for confirmation>t
<output>
svn log url
<svn prompt for confirmation>t
<output>
echo t | svn log url
<output>
What exactly is the difference between reading from the owner terminal and stdin? I think that I was under the impression that the owner terminal would be writing to stdin, though I admit that my understanding of terminals is abstract and incomplete...
Pipe the output of the svn export to a temporary file so you can see what exactly it is outputting. Also, have you used --non-interactive?
What exactly is the difference between reading from the owner terminal and stdin?
One can be redirected to a file, and the other is the terminal? I don't know if Windows works the same way, but it probably doesn't.
It seems to be working (at least in part) to specify the --config-dir option, but now I'm getting the following output to svn's stderr stream (by memory):
Authentication realm: (url) authentication_realm_name
It doesn't really resemble a prompt, but there is no further output and the program seems to hang there... When a colleague with server access executes the command from the Command Prompt it executes successfully and the above message isn't displayed (unless stderr isn't display by default in Windows, in which case I don't know if this message is written to stderr for the colleague). If it's not a prompt, then my only explanation is that it's actually just Apache being informative and my debugging code is blocking...
// Debugging code to try to see why/if svn is failing. string line; Program.StartLog("svn stderr:\n\n"); while((line = svn.StandardError.ReadLine()) != null) Program.Log(String.Format("{0}\n", line)); Program.StartLog("svn stdout:\n\n"); while((line = svn.StandardOutput.ReadLine()) != null) Program.Log(String.Format("{0}\n", line));
Does anybody know if this is the right/wrong way to read from the streams in C#? I've noted in examples that it seems the Flush() or Close() method needs to be called for some reason, but I don't really understand how you would go about reading again (as some programs would dictate) if the stream is closed... I don't really understand how to check if there is data to be read from a particular StreamReader; if so read it line by line and process it; and if not continue on with the program...
** EDIT **
I removed the above code from the program (I think) and it's still blocking on the svn process... I can't imagine any reason for svn to take more than a second to execute (it's only processing one file) unless it's prompting...
Pipe the output of the svn export to a temporary file so you can see what exactly it is outputting.
I'm not sure how I would do that in C#...
Also, have you used --non-interactive?
I originally wanted to use that option, but before I understood the --config-dir option I was trying to figure out how to respond to a prompt... I guess now I can go back to --non-interactive mode and hope that svn either succeeds or writes something useful to stderr.
I'm not sure how I would do that in C#...
You would not use C#, you would use the shell's output piping: command > program.log
You would not use C#, you would use the shell's output piping: command > program.log
Are you talking about svn's output when executed by my program or by my colleague from the command line? My colleague copied the output from svn to me and it was the standard successful output...
prompt>svn export arguments flags file Export complete.
From my program, svn is started with System.Diagnostics.Process.Start(ProcessStartInfo). The ProcessStartInfo instance has a FileName property (i.e. the executable) and an Arguments property. I'm not sure if it would work to add redirection arguments (especially with the UseShellExecute property set to false)...
I did try reading from the svn output streams and writing them to files:
1 | StreamWriter svn_stderr = null; |
2 | StreamWriter svn_stdout = null; |
3 | |
4 | try |
5 | { |
6 | svn_stderr = new StreamWriter(@".\svn.stderr"); |
7 | svn_stderr.Write(svn.StandardError.ReadToEnd()); |
8 | } |
9 | catch(Exception) |
10 | { |
11 | Program.PrintError(msg = "Failed to redirect svn stderr.\n"); |
12 | Program.StartLog(msg); |
13 | } |
14 | finally |
15 | { |
16 | svn_stderr.Close(); |
17 | svn_stderr = null; |
18 | } |
19 | |
20 | try |
21 | { |
22 | svn_stdout = new StreamWriter(@".\svn.stdout"); |
23 | svn_stdout.Write(svn.StandardOutput.ReadToEnd()); |
24 | } |
25 | catch(Exception) |
26 | { |
27 | Program.PrintError(msg = "Failed to redirect svn stdout.\n"); |
28 | Program.StartLog(msg); |
29 | } |
30 | finally |
31 | { |
32 | svn_stdout.Close(); |
33 | svn_stdout = null; |
34 | } |
Which seems to work on my local system, but is apparently never reached on the server... If there was an uncaught exception (while being executed by a user with no logon) what should happen to my program...? Would Windows terminate it or would it just stop executing and hang?
You have a batch file, called post-commit.bat. In it, you run svn export <parameters> > C:\WINDOWS\Temp\post-commit.log You then review post-commit.log and find why your stuff doesn't work.
You have a batch file, called post-commit.bat. In it, you run svn export <parameters> > C:\WINDOWS\Temp\post-commit.log You then review post-commit.log and find why your stuff doesn't work.
That's brilliant! I can't believe I didn't think of running svn from the batch file...
Didn't I say that in another one of your threads?
Didn't I say that in another one of your threads?
I'm not sure... Not that I can find... You did suggest that I call a more powerful script from my batch file, but instead I've opted to write a simple C# program to handle it. That's all I can find...
I think the more powerful program was meant for if you need to delete a file, as if you delete/rename in SVN, the exported copy would still have the last version, and if it's a file that needs to be deleted...
By executing svn from the batch file, the following was output to stderr:
svn: PROPFIND request failed on 'file' svn: PROPFIND of 'file': authorization failed (url)
stdout was empty.
Personally, I would check out a working copy, use htaccess do deny any access to anything .svn, and then just issue a svn update.
You used --config-dir and --non-interactive? You should run it manually and add in the user and password information, since it seems it couldn't ask you for them.
Personally, I would check out a working copy, use htaccess do deny any access to anything .svn, and then just issue a svn update.
You're assuming the Web server is Apache.
You used --config-dir and --non-interactive? You should run it manually and add in the user and password information, since it seems it couldn't ask you for them.
Yes, both options were used. The user authentication information should already be cached... A colleague with server access can run svn export with the --config-dir option and it executes successfully without any prompts...
** EDIT **
Executing the exact same svn export command as the batch file (with the --non-interactive option)(and to be paranoid, my colleague's personal "%APPDATA%\Subversion" directory deleted), she is getting the same stderr output as the hook script.
** EDIT **
Without the --non-interactive option apparently it just sits there...
svn export --force --config-dir "directory" "file_url" "file_path" \ 1>"svn-stdout.txt" 2>"svn-stderr.txt"
** EDIT **
As an administrator, it executes fine.
** EDIT **
Actually, svn wasn't just sitting there... It was waiting for her to enter the password. The username/password prompt was being redirected to files...
You should run it manually and add in the user and password information, since it seems it couldn't ask you for them.
You appear to be correct... I thought we already saved the user credentials, but apparently not... So we're doing it again now... I'm about to find out if it worked...
** EDIT **
Now the hook script fails and none of the stream files exist.
Run it as the user it will be run as manually, without the redirection.
Run it as the user it will be run as manually, without the redirection.
I don't even know if that's possible... The user has no logon, whatever that means...
Anyway, it seems that when I sent the latest revision of my program to replace the currently "installed" one I sent it with a .txt extension appended. My colleague forgot to rename it so the batch file wasn't able to find my program, which caused it to fail and no output to be captured... After correcting that, I got log messages indicating that svn exited with a non-zero exit status. When I left work I had just modified the program to capture svn's output. Tomorrow I'll see if that yields anything useful.
You can still run it as that user.
Just so we're clear, the only purpose of the C# program is to run svn export? And you are running the C# program from a batch file? If that is actually the case, then what you are doing is almost worse-than-failure-worthy.
On the topic of actually making the solution work: if it's asking for a user name and password, provide one. The commands are quite clearly shown in svn help export.
You can still run it as that user.
IIRC, I asked my colleague to do that and either it failed because "the user account doesn't have a logon" or my colleague didn't know how to do it (or I misinterpreted her)...
Just so we're clear, the only purpose of the C# program is to run svn export? And you are running the C# program from a batch file? If that is actually the case, then what you are doing is almost worse-than-failure-worthy.
The C# program only exports files that have changed in the most recent commit (as well as creating directories and deleting files/directories) as indicated by svnlook changed. There are thousands files which as you can expect would take a long time to export each time. Obviously svn update would be a nice, simple solution, but then you have the .svn directories which are undesired, especially on remote, third party servers. There may be better ways to do what I'm doing, but this is the solution I've come up with...
Just hide the .svn directories. Can't be that hard, no matter what you're using.
Just hide the .svn directories. Can't be that hard, no matter what you're using.
I tried to Google it and came up mostly empty... Anybody know how to configure IIS to NOT serve directories based on name alone? I don't know if the higher ups would go for this anyway so it's only a partial/alternative solution. I've still gotta get my program to interface properly with Subversion.
How about just do an svn update locally and then copy all the files that aren't named ".svn"?
How about just do an svn update locally and then copy all the files that aren't named ".svn"?
We'd rather an automated process... It should be more reliable and less error-prone (I hope).
** EDIT **
The output from svn, when executed by my program/hook script, is...
svn: PROPFIND request failed on 'relative_file_path' svn: PROPFIND of 'relative_file_path': authorization failed (url)
My colleague can run the same command fine without any prompts or errors. WTF!?
We'd rather an automated process... It should be more reliable and less error-prone (I hope).
It's pretty easy: you already have this:
The C# program only exports files that have changed in the most recent commit (as well as creating directories and deleting files/directories) as indicated by svnlook changed.
So just takes those files, and copy them using the C# program, instead of using svn export. Maybe a mapped network drive, maybe FTP, maybe WebDAV, whatever you like.
So just takes those files, and copy them using the C# program, instead of using svn export. Maybe a mapped network drive, maybe FTP, maybe WebDAV, whatever you like.
Copy them from where? They're in a Subversion repository until they get exported out... I'm not sure I understand what you're suggesting. If my colleague manually executes the hook script with the same arguments that Subversion passes it, it works flawlessly. It seems the only problem is with user permissions on the server.
Maybe you think that svn export can store the files on a remote destination? It can't.
I'm going to start from scratch. You have a subversion repository and a web server. These are on different machines. You need the web server to be updated when the subversion repository is updated.
I would solve this problem using a post-commit hook, like you are trying to do. The post commit hook obviously runs on the machine with the subversion repository.
The script should maintain a local copy of the repository (updated with svn update). When the script is executed, it should update the repository, then copy all of the files in the local repository to the remote server, ignoring the subversion special directories.
As an example, this would be the script, using lftp to send the files over unencrypted FTP.
cd C:\subversion\www_files
svn up
lftp ftp://user:password@host -e "mirror -nvR --delete -x \.svn . ."
This is based on a script I use to update a static HTML web site that uses a subversion repository to maintain its files. The script only updates files that have changed, as based on the time stamp and file size. The update process is slow if there are a large number of directories in the repository (lftp must look in each one on the remote server).
If you can access the files locally (mapped network drive or something), instead of using FTP, you could use rsync, might be faster...
Well the server is running Windows Server and the Subversion server and Web server are both running on the same machine with "local access" to the repositories and document roots. I need a Windows solution and I'm pretty sure I was told that Cygwin-/MSYS-like tools are not an option.
Have you tried using svn export using the local paths then? Obviously this isn't the solution you should use in the long-term, but does it work?
If you have access to the wwwroot locally, you can replace lftp with xcopy. It's seriously the easiest thing ever, after I just tried it.
xcopy can recursively (/E) copy a directory and by default ignores hidden files (.svn directories are hidden). xcopy can also be made to only copy files with the archive bit set (/M; the archive bit is set by writing to the file). It can be forced with /Y. xcopy also has a decent file exclusion pattern, but it won't be needed if all you want to do is ignore the hidden files and directories. So, the solution:
REM assumes that a checked out repository is in C:\inetpub\trunk REM and that the webroot is C:\inetpub\wwwroot cd C:\inetpub svn up trunk xcopy /M /E /Y trunk wwwroot
By using /M, xcopy will copy all files that have been written, then clear the archive bit, so only changed files will be written after that point. Note that this will not handle deletion of files.
Have you tried using svn export using the local paths then? Obviously this isn't the solution you should use in the long-term, but does it work?
No, I hadn't. It works fine with a file:/// url. Why isn't this a solution to use in the long-term though?
Am I missing something?
If you are using subversion export with an https repository URL and a local path for the local url, then you should be fine. I think the svn up idea is a better solution though.
Currently, I'm using subversion export with a file repository URL (file:///) and a local path (<tt><DriveLetter>:</tt>). Is there anything particularly wrong with it (i.e. security holes or performance)?
The svn up idea does sound better, but it would still need a wrapper of some sort to send particular branches to particular web roots (i.e. current development branch, current testing branch) automatically when they are committed. As is, a simple configuration file is used that matches branches to web roots... That was pretty much why I moved it into a C# program.
Is there anything particularly wrong with it (i.e. security holes or performance)?
I'm going to go with file locking issues because you are having two processes access the database at the same time. It is probably fine, but personally I'd prefer to use the web URL if it were possible.
As is, a simple configuration file is used that matches branches to web roots... That was pretty much why I moved it into a C# program.
Yeah, I figured that was what was going on, but you were being pretty vague
Have you tried saving the users in a file and then redirect stdin to the execution command?
svn .... < bleh.txt
Have you tried saving the users in a file and then redirect stdin to the execution command?
svn .... < bleh.txt
I think we've already solved the problem you're offering solutions for. Thanks though.
Currently, the only problem appears the be the inability to use an https:// url for the repository. It seems to work with a file:/// url, but for various reasons I'd prefer to get https:// working.
I assume the reason https:// doesn't work is because the user account running the Subversion server (which executes the hook script) has limited permissions, but I don't know much about Windows Server, its user accounts, or the actual user's configuration.
i do this process/stdin stuff all the time with just plain C.