Search code examples
svnsvn-externals

Automatically updating svn:external


I'm splitting an SVN repository into two: project1, and common. This way, I can also make project2 which also depends on common.

project1 will contain an svn:external link to common. I'd like people to constantly develop against the HEAD of common, but literally pegging an svn:external to HEAD causes problems (e.g. tags will reference the current HEAD instead of the HEAD at the time of tagging). Manually pegging a static revision is annoying because if we don't update project1 for a while, then it's manager's will be more reluctant to take all of the changes from common as untested changes become a risk. That leads to forking which we want to avoid.

How can one work around this?


Solution

  • Run this script as a cron-job or systemd.timer from a linux server (using an account with write access to the repository).

    This solves the problem where reverting to an older revision of project1 will also revert to the version of common used at that time by constantly updating the pegged svn revision.

    As an added bonus, if someone updates common, all projects running this script will automatically commit an external property update which can trigger CI (using something like Jenkins). That means a commit to common will immediately be tested on all dependent projects.

    #!/bin/bash
    
    # Updates all svn externals in a remote repository
    # argument 1 is the server
    # argument 2 is the path to the externals directory relative to the server root
    #
    # Note: all externals in the repository must be relative to the server root.
    # See `svn help pset`
    #
    # Example:
    # If you have externals in https://example.com/svn/project1/trunk, then:
    #
    # this-script https://example.com /svn/project1/trunk
    #
    
    server=$1
    repo=$2
    
    # Get the commited properties
    svn checkout $server$repo wc --depth=empty
    svn pget svn:externals wc > wc.externals
    
    # Remove empty lines
    sed -i '/^$/d' wc.externals
    
    # for each external in existing properties:
    while read line; do
    
      # tokenize
      IFS='@ ' tokens=( $line )
    
      # extract interesting stuff from the tokens
      extrepo=${tokens[0]}
      rev=${tokens[1]}
      dir=${tokens[2]}
    
      # Query the external repository for the last change
      latest=$(svn info $server$extrepo | sed -ne 's/^Last Changed Rev: //p')
    
      # Write a new set of externals based on latest info
      echo "$extrepo@$latest $dir" >> wc.newexternals 
    
    done <wc.externals
    
    # Get the differences between the new and old externals
    details=$(diff -y --suppress-common-lines wc.externals wc.newexternals)
    
    # If differences exist (and the diff was valid)
    retval=$?
    if [ $retval -eq 1 ]; then
    
      # Set the new properties
      svn pset svn:externals -F wc.newexternals wc
    
      # Generate the SVN log
      echo "Automatic externals update" > wc.log
      echo $details >> wc.log
    
      # Commit the changes
      svn ci -F wc.log wc
    fi
    
    # Cleanup
    rm -rf wc wc.externals wc.newexternals wc.log
    

    The limitation is that it only supports externals configured relative to the same server's root.