Google
 
Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Friday, April 4, 2014

Automated SVN Deployment

After much gnashing of teeth, I've finally managed to build a bash script to automate deployment of my builds to production/staging for my in-progress website.

Note that the SVN hooks directory, as well as the working directories for the SVN must all be owned by the user that the hook script is executing as. In my case, the user was www-data, so I needed to run chown -R www-data /var/svn/repo/hooks, chown -R www-data /var/www/production, and chown -R www-data /var/www/staging. This also needs to be a post-commit script in order to function properly. Staging and production must also be manually set up as working directories. Staging should be on /trunk, but it doesn't particularly matter what production is set to, as it will soon change.

Now, my script isn't the most secure in the world, this I know. Mainly, because I've got the SVN username and password stored in plaintext in the script. At this point, though, I don't care. Here's the script!

#!/bin/sh
USER='username'
PASS='password'
REPOS="$1"
TXN="$2"

/usr/bin/svn update /var/www/staging --non-interactive --trust-server-cert --username-"$USER" --password="$PASS"

SVNLOOK=$(/usr/bin/svnlook history -r "$TXN" "$REPOS" /tags | grep "$TXN")

if [ -n "$SVNLOOK" ]; then
TAG=$(/usr/bin/svn ls "file://$REPOS@$TXN" "^/tags" | tail -1)
/usr/bin/svn switch "file://$REPOS/tags/$TAG" /var/www/production --username="$USER" --password="$PASS"
fi

What It Does:

$1 and $2 are the parameters automatically passed to the post-commit; the name of the repository and the revision number just committed, respectively. First, the staging site is updated to the most recent revision of the trunk (--non-interactive --trust-server-cert is used because my certificates have bad validation :P). Then, the script looks at the SVN history for the current revision, checking whether there's anything in the /tags directory. If there is a change to anything in /tags for this revision, we obtain the most recent folder, and switch to it on production.

The Result:

When you commit a change to the repository, the staging site is updated. When you tag a revision, the production site is updated. Thus, staging will always have the most recent (though possibly broken) build of the application, and production will always have production builds.

Friday, September 2, 2011

Android Resources: Map Structures

Resource files host a very limited set of data types for Android developers. In a recent project, I wanted to have a mapped data set (in Java, the data type would have been Map<String, Map<String, Integer>>). I looked around the internet trying to find out how to do it, and the resounding answer was that it's not possible. Options include creating a <string-array> and an <integer-array>, and merging them into a Map at runtime, or alternatively creating a series of <string> resources, and merging them into a Map using reflection.

In my opinion, neither is a particularly good option for creating a resource, when the supported types work so seamlessly. So, I spent time developing a workaround. It turns our that the <array> type is functionally similar to Object[]. Each <item> can be any simple type (int, string, etc.), or else a reference to an existing resource (of any type). This means you can in fact have a nested array, you simply have to write each array separately and then use a reference in one to the other.

<resources> <array name="my_array"> <item>@array/a0</item> <item>@array/a1</item> <item>@array/a2</item> </array> <array name="a0"> <item>0,0</item> <item>0,1</item> <item>0,2</item> </array> <array name="a1"> <item>1,0</item> <item>1,1</item> <item>1,2</item> </array> <array name="a2"> <item>2,0</item> <item>2,1</item> <item>2,2</item> </array> </resources>

Unfortunately, you can't simply load the resource as a Map or Object[][]. It's a little more complicated than that. You've got to load the TypedArray ('my_array'), loop through the values, obtain the resourceId for each of those values, use the resourceId to load the sub-array as another TypedArray, and then loop through those values. Further levels adds more steps of the same actions.

TypedArray myArray = getResources().obtainTypedArray(R.array.my_array); int columns = myArray.length(); for(int i = 0; i < columns; i++) { int rowid = myArray.peekValue(i).resourceId; TypedArray row = getResources().obtainTypedArray(rowid); int values = row.length(); for(int k = 0; k < values; k++) { System.out.print(row.peekValue(i).coerceToString() + "\t"); } System.out.println(); }

I have not tried creating array which reference each other, but it's definitely a bad idea. I suspect it'll result in a compile-time error, but I don't intend to try.

Monday, March 7, 2011

Networking Sucks

I'm currently working on a game project with networking. What should have a simple P2P topology has evolved into something so arcane, I can't make out what half out code is doing.

Stupid networking...

Tuesday, June 29, 2010

Java Callbacks using java.lang.reflect

Java does not have callbacks. Most commonly, I see people working around this by implementing the Observer Pattern, or simply having an interface for each method they need to use as a callback.

On a recent project, I felt the need for more direct callbacks in my Java code. Java's reflection package seemed to hold my answer. Given a Class, a method name, and (in the case of overloaded methods) a list of parameter types, you can retrieve an instance of the Method class. Then, given a list of arguments to pass to the method, it can be invoked via reflection.

Java reflection isn't particularly fast, so I recommend against this approach in applications where performance is important. But reflection works, so I present my Callback class here: package com.bs; import java.lang.reflect.*; /** * Implements callback functionality for Java. * Callbacks are implemented using reflection, so should * be avoided if possible. * * @author Brian Shields <http://clockworkgear.blogspot.com/> */ public class Callback { private Object parentObj; private Method method; private Class<?>[] parameters; public Callback(Class<?> clazz, String methodName, Object parentObj) { // Find a method with the matching name Method[] allMethods; try { allMethods = clazz.getMethods(); } catch(SecurityException se) { allMethods = new Method[0]; } int count = 0; Method single = null; for(Method m : allMethods) { if(m.getName().equals(methodName)) { single = m; count++; } // Can't have more than one instance if(count > 1) throw new IllegalArgumentException(clazz.getName() + " has more than one method named " + methodName); } if(count == 0) // No instances found throw new IllegalArgumentException(clazz.getName() + " has no method named " + methodName); this.parentObj = parentObj; this.method = single; this.parameters = single.getParameterTypes(); } public Callback( Class<?> clazz, String methodName, Object parentObj, Class<?>...parameters) { try { this.method = clazz.getMethod(methodName, parameters); } catch(NoSuchMethodException nsme) { nsme.printStackTrace(); } catch(SecurityException se) { se.printStackTrace(); } this.parentObj = parentObj; this.parameters = parameters; } public Object call(Object...vals) { if(parameters.length != vals.length) throw new IllegalArgumentException( "Wrong number of method parameters given. Found " + vals.length + ", expected " + parameters.length); Object ret = null; try { ret = method.invoke(parentObj, vals); } catch(IllegalAccessException iae) { iae.printStackTrace(); } catch(InvocationTargetException ite) { ite.printStackTrace(); } return ret; } }

Tuesday, April 20, 2010

The Tao of Game Features

This quote was taken from "Arcanaville" on the City of Heroes forums, with regard to adding new features to the game. The quote is lovely, and I think it applies to just about any update to any game, or software in general.

It's never as hard as the developers say it is, it's never as easy as the players think it is, and the best way to do it is most likely to be a way both groups would initially think is insane.

Sunday, January 4, 2009

Source Code Sucks

By which I mean, looking at source code on most websites sucks. The formatting is often broken, and there's rarely syntax highlighting. This post was prompted by a comment from an author of another blog that Blogger screws up source code. That's not the problem; the problem is that a lot of time and money has been poured into <insert favorite IDE>, while the same effort is not warranted to web browers and most websites.

However, with a little finesse, there is a solution that's easy to implement. You need:

  1. CSS for the code box and syntax highlighting
  2. A good find/replace JavaScript
  3. Code samples
Alex Gorbatchev of dreamprojections.com has created an excellent Syntax Highlighter script. (download link)

An Example

<html> <head> <link rel="stylesheet" type="text/css" href="dp.SyntaxHighlighter/Styles/SyntaxHighlighter.css" /> <script type="text/javascript" src="dp.SyntaxHighlighter/Scripts/shCore.js"></script> <script type="text/javascript" src="dp.SyntaxHighlighter/Scripts/shBrushCSharp.js"></script> <script type="text/javascript"> <!-- window.onload = function() { var codeBlocks = document.getElementsByName('csharp'); for(var i = 0; i < codeBlocks.length; i++) { codeBlocks[i].className = 'csharp'; } dp.SyntaxHighlighter.ClipboardSwf = 'dp.SyntaxHighlighter/Scripts/clipboard.swf'; dp.SyntaxHighlighter.HighlightAll('csharp',false,false,false,1,false); } //--> </script> </head> <body> <code name="csharp">using System; using System.Collections.Generic; using System.Text; namespace HelloWorld { class Program { static void Main(string[] args) { } } }</code> </body> </html>

This would produce:using System; using System.Collections.Generic; using System.Text; namespace HelloWorld { class Program { static void Main(string[] args) { } } }

The Google Code wiki for the JavaScript Syntax Highlighter shows how to modify settings of the code block through the class attribute. The same configuration can be done with the HighlightAll call, using the following parameters:HighlightAll(name, showGutter /* optional */, showControls /* optional */, collapseAll /* optional */, firstLine /* optional */, showColumns /* optional */)