SVN

From Pickwiki
Revision as of 20:06, 9 April 2014 by James (talk)
Jump to navigationJump to search

HomePage>>SourceCode>>BasicSource>>SvnWrapper>>SVN

SVN Source code; once all source is compiled and cataloged, 'SVN -H' will show usage.

* ===================================== SVN =============================================
* @TCL: SVN wrapper, to be run on the UV prompt
* =======================================================================================
* 04.Oct.2013 james: Check /released/ folder too when doing STATUS
* 20.Dec.2012 james: Add CATALOG
* 07.Sep.2012 james: New 'COMMENTS' option to see the latest comment for each program.
* 12.May.2011 james: Handle 'DICT/' in release (flist) files.
* 10.Aug.2010 james: Add 'COMPILE' option to check a release before it goes out.
* 07.Oct.2009 james: Add 'UPDATE'; only add -m if 'RequiresComment'.. IS THIS OK !?
* 30.Jun.2009 james: Add 'CheckReleaseStatus' to make sure nothing was committed after
*                    the Release's Revision Number.
* 17.Nov.2008 chrisg: Added support for CLEANUP command
* 12.Jul.2008 chrisg: Added support for SVN PROPSET command
* 22.Apr.2008 manoj: Fixed a bug that limit the length of a new comment to the comment on 
*                    line 4
* 11.Apr.2008 jim: Use the line 4 comment for the CommitComment ONLY IF there's one item.
* 11.Feb.2008 jim: RequiresFileList = @FALSE for DELETE and STATUS; so those still work
*                  if files are in SVN but not in UV.
* 14.Jan.2008 manoj: Added DELETE
* 10.Jan.2008 manoj: Added revert
* 02.Jan.2008 jim: Restructure to improve error handling, etc.
* 31.Aug.2007 manoj: Created
* =======================================================================================
* $Id: SVN 44131 2013-11-12 02:57:36Z jim $
* =======================================================================================
*
* This invokes the svn command, with the proper svn URL and REAL.USER name
*
* Ex:
*
*  svn commit svn+ssh://manoj@svn.docmagic.com/opt/svn/dsi/Database/Universe/trunk/dsisrc
*
* Trac Reference: http://trac/dsi/wiki/UniverseInSvn
*
* =======================================================================================

  $include RMS.BP EQU.CMDLINE

  equate SVN$CMD          to "/usr/bin/svn"

  deffun GET.REALUSER( Err )
  deffun OPEN.FILE( FILENAME, FLAGS, FILEVAR, ERR.MSG )

  username = GET.REALUSER( Err ) ; if Err # '' then stopm Err  ;*  Err not yet implemented anyway
  logFile  = "log/SVN.LOG"

  gosub ParseCommandLine
  gosub PrepareSvnCommand

  if Err # '' then
    gosub DisplayErrors
  end else

    gosub ExecuteSvnCommand
    gosub ExecutePostSvnCommands

  end
return

* =======================================================================================

PrepareSvnCommand:
  RequiresFileList = @FALSE
  RequiresComment  = @FALSE
  * Also add svnOp below (in the ShowHelp )
  begin case
    case svnOp = "COMMENTS" ; RequiresFileList = @TRUE  ;*  NOT REALLY AN SVN COMMAND BUT IT'S HANDY !
    case svnOp = "BASIC"    ; RequiresFileList = @TRUE  ;*  NOT REALLY AN SVN COMMAND BUT IT'S HANDY !
    case svnOp = "COMPILE"  ; RequiresFileList = @TRUE  ;*  NOT REALLY AN SVN COMMAND BUT IT'S HANDY !
    case svnOp = "CATALOG"  ; RequiresFileList = @TRUE  ;*  NOT REALLY AN SVN COMMAND BUT IT'S HANDY !
    case svnOp = "COMMIT"   ; RequiresFileList = @TRUE  ; RequiresComment = @TRUE
    case svnOp = "ADD"      ; RequiresFileList = @TRUE
    case svnOp = "UPDATE"   ; RequiresFileList = @TRUE  ;*  Is this dangerous !? Just for messed up SVN !?!
    case svnOp = "DELETE"   ; RequiresFileList = @FALSE ;*  Allow delete from SVN with no UV item ?
    case svnOp = "DIFF"     ; RequiresFileList = @TRUE
    case svnOp = "INFO"     ; RequiresFileList = @TRUE
    case svnOp = "LISTS"    ; RequiresFileList = @TRUE  ;*  NOT REALLY AN SVN COMMAND BUT IT'S HANDY !
    case svnOp = "LOG"      ; RequiresFileList = @TRUE
    case svnOp = "REVERT"   ; RequiresFileList = @TRUE
    case svnOp = "PROPSET"  ; RequiresFileList = @TRUE
    
      if PropertyNameAndValue = '' then 
        Err = svnOp:" requires -P, see help"; return
      end else
        PropertyNameAndValue = EREPLACE(PropertyNameAndValue, "=", " ")
      end

    case svnOp = "STATUS"  ; * FileList is NOT required; but use it if it's there.

      if FileList = '' then
        if FileName # '' then
          FileList = FileName
        end else
          FileList = "DSI.BP DA.BP WEB.BP RMS.BP"
        end
      end else Verbose = @TRUE ;*  Force Verbose if you list specific items.
      if Verbose then FileList := " -v"  ;*  Always VERBOSE (show lines NOT changed too...)
      CommitComment = ''   ;*  May have been set from 'ReadRelease' ?
      
    case svnOp = "CLEANUP" ; FileList = FileName
    case svnOp = "HELP"    ; FileList = downcase(FileName) ;*  NO options required; but may have 2nd parameter
    case 1
      Err = "Invalid SVN Operation '":svnOp
  end case

  if FileErrs # '' then crt FileErrs ; input j

  if RequiresFileList then
    Err = FileErrs ; if Err # '' then return
    if FileList = '' then Err = svnOp:" requires a File and Item name" ; return
  end

  if RequiresComment and CommitComment = '' then
    if CMD.NUM.ARGS = 3 then  ;*  Only 1 file to commit: use it's comment (Tacky ?) :
      FileName = CMD.ARG.S<1, 2>
      ItemID   = CMD.ARG.S<1, 3>
      open FileName to File then
        read ProgramItem from File, ItemID then
          CommentLine = ProgramItem<4>
          CommentUser = field( CommentLine, ' ', 3 )
          CommitComment = CommentLine[ col2() + 1, 999 ]
        end else CommitComment = ""  ;*  Why would this be ? Throw an error ?
      end
    end
    gosub GetCommitComment
    if CommitComment = "" then Err = "Cannot ":svnOp:" with an empty comment"
  end
return

ExecuteSvnCommand:
  *  Force the username by passing to the "sudo" command below
  svnCommand = SVN$CMD:' ':downcase(svnOp):' ':PropertyNameAndValue:' ':FileList
  if RequiresComment and CommitComment # '' then
    svnCommand := ' -m "':CommitComment:'"'
  end

  begin case
    case svnOp = "COMMENTS"  ;*  Special hack: just to use the code that reads the Release

      crt
      crt " (comments are always from the CURRENT version...)"
      crt
      FileList = trim( FileList )
      NumFiles = dcount(FileList, ' ')

      LongestName = 0
      for N.F = 1 to NumFiles
        OneProgram = field( FileList, ' ', N.F )
        if len(OneProgram) > LongestName then LongestName = len(OneProgram)
      next N.F
      NameFmt = 'L#':LongestName

      for N.F = 1 to NumFiles
        OneProgram = field( FileList, ' ', N.F )
        ProgramFile = field( OneProgram, '/', 1)
        ProgramID   = field( OneProgram, '/', 2)
        if not( OPEN.FILE( ProgramFile, '', PROGRAM.FILE,  Err ) ) then stopm Err
        read ProgramCode from PROGRAM.FILE, ProgramID else crt "No item named:  ":ProgramID ; return
        crt OneProgram NameFmt:' ':ProgramCode<4>
      next N.F
      crt

    *  Special hack: just to use the code that reads the Release
    case svnOp = "COMPILE" or svnOp = "BASIC" or svnOp = "CATALOG"

      FileList = trim( FileList )
      NumFiles = dcount(FileList, ' ')
      for N.F = 1 to NumFiles
        OneProgram = field( FileList, ' ', N.F )
        convert '/' to ' ' in OneProgram
        if svnOp = "COMPILE" or svnOp = "BASIC" then TclCommand = "BASIC" else TclCommand = "CATALOG"
        execute TclCommand:" ":OneProgram
      next N.F

    case svnOp = "LISTS"  ;*  Special hack: just to use the code that reads the Release

      gosub SaveProgramLists

    case 1

      svnScript  = "(cd /u/dsisrc; sudo -u ":username:" ":svnCommand:" )"
      call APPEND.UNIX.LOG (svnScript, logFile, aulErr)

      if DisplayCmd then
        crt svnScript ; crt
        input j ; j = upcase(j)
        if j = 'X' or j = 'Q' then stop
      end

      if not(NoExecute) then
        execute "SH -c \":svnScript:"\" capturing SvnResponse
      end
      OutputResponse = SvnResponse
      convert @FM to char(10) in OutputResponse
      crt OutputResponse

  end case

return

ExecutePostSvnCommands:
  *  Now do anything special we need 'post SVN' :
  begin case

    case svnOp = "DIFF"
      if SvnResponse = '' then crt "The current version does not differ from what's checked in." ; crt

    case svnOp = "COMMIT"
      if ReleaseNumber # '' then  ;*  Separate 'if' because SvnLine will only be defined for -R :
        if SvnLine # 0 then       ;*  Release needs the SVN number:
          gosub AddReleaseNumber
        end
      end

    case svnOp = "STATUS"
      if ReleaseNumber # '' then  ;*  SvnLine will only be defined for -R :
        gosub CheckReleaseStatus
      end

  end case
return

SaveProgramLists:
  if not(OPEN.FILE("&SAVEDLISTS&", "", SAVEDLISTS.FILE, Err)) then stopm Err
  FileList = trim( FileList )
  NumFiles = dcount(FileList, ' ')
  F.P = 0 ; CurrentFile = '~' ; ItemList = ''
  loop
    F.P += 1
   while F.P <= NumFiles do
    OneProgram = field( FileList, ' ', F.P )
    ProgramFile = field( OneProgram, '/', 1 )
    ProgramItem = field( OneProgram, '/', 2 )
    if ProgramFile # CurrentFile then
      gosub SaveLastList
      CurrentFile = ProgramFile
    end
    ItemList<-1> = ProgramItem
  repeat
  gosub SaveLastList
return

SaveLastList:
  if CurrentFile # '~' then
    ListName = CurrentFile:"_":ReleaseNumber
    crt "Writing list ":ListName
    write ItemList on SAVEDLISTS.FILE, ListName
    ItemList = ''
  end
return

* =======================================================================================

AddReleaseNumber:
  RevisionNumber = ''
  NumResponseLines = dcount( SvnResponse, @FM )
  for N.R.L = NumResponseLines to 1 step -1  ;*  Go backward since it's at the end:
    ResponseLn = SvnResponse<N.R.L>
    if ResponseLn[1,18] = "Committed revision" then
      RevisionNumber = trim( field(ResponseLn[19,999], '.', 1) )
    end
  next N.R.L
  if RevisionNumber # '' then
    ReleaseText<SvnLine> = "SVN: ":RevisionNumber
    convert @FM to char(10) in ReleaseText
    *  Was "/home/release/":ReleaseNumber:"/flist" but maybe in 'released' now?:
    call WRITE.UNIX( ReleaseText, flistPath, Err)
  end
return

*  SVN STATUS -R xxxx : Check if the SVN # of each item is > the Release's #

CheckReleaseStatus:
  RevisionNumber = field( ReleaseText<SvnLine>, ' ', 2)
  if RevisionNumber = '' then return

  if ReleaseLocation # '' then crt ; crt ReleaseLocation ; crt

  crt ; crt "Checking that each file is before Revision ":RevisionNumber:'...' ; crt
  NumResponseLines = dcount( SvnResponse, @FM )
  for N.R.L = 1 to NumResponseLines
    ResponseLn = SvnResponse<N.R.L>
    Revs = trim( ResponseLn[12, 9999] )  ;*  Kind of a hack ? How else though ?
    LastCommitted = field(Revs, ' ', 2)
    if LastCommitted > RevisionNumber then
      Path = field( Revs, ' ', 4)
      crt Path:" has a Revision Number (":LastCommitted:") AFTER this Releases Revision # ":RevisionNumber:" ! (one-off Commit ?)"
    end
  next N.R.L
  crt
return

DisplayErrors:
  if Err # '' then
    crt
    crt 'ERROR ! : ':Err<1>
    NumErrs = dcount(Err, @FM)
    for N.E = 2 to NumErrs
      crt '          ':Err<N.E>
    next N.E
    crt
    crt "   ('SVN -H' for more info)"
    crt
  end
return

ReadRelease:
  ReleaseLocation = ''
  flistPath = "/home/release/":ReleaseNumber:"/flist"
  call READ.UNIX( ReleaseText, flistPath, Err)
  if Err # '' then
    ReleaseLocation = ' ** RELEASED **'
    flistPath = "/home/release/released/":ReleaseNumber:"/flist"
    call READ.UNIX( ReleaseText, flistPath, Err2)
    if Err2 # '' then
      crt Err ; crt Err2 ; input j ; return
    end
    Err = ''  ;*  Must be in 'released'... so we're good.
  end

  convert char(10):char(13) to @FM in ReleaseText
  SvnLine = 0 ;* Track the 'SVN:' line to append the # if we are Committing and there's not one already
  NumLines = dcount( ReleaseText, @FM )
  for N.L = 1 to NumLines
    LN = trim( ReleaseText<N.L> )
    begin case
      case LN[1,2] = '**' and CommitComment = ''
        CommitComment = LN[3, 99999]       ;*  Special single comment line used for Commit
      case LN[1,1] = '*' or LN[1,1] = '#'  ;*  Ignore comments
      case index(LN, ':', 1) # 0           ;*  Handle special tags :
        if LN[1,4] = "SVN:" then           ;*  Is this the "SVN:" line ?
          SvnLine = N.L                    ;*  Remember where
        end
      case LN # ''                         ;*  Finally handle normal lines:
        Item = LN ; gosub AddItem
    end case
  next N.L
return

AddItem:
  FileName = field(Item, '/', 1) ; ItemID = field(Item, '/', 2)
  open FileName to File then
    FileList := Item:' '  ;*  Keep list because some things don't need to be in UV ?:
    read dummy from File, ItemID else
      FileErrs<-1> = ItemID:" is not in ":FileName
    end
  end else
    if FileName = 'DICT' then  ;*  Special case: make it act like a BP ?
      FileList := Item:' '     ;*  Just like above ?
    end else
      FileErrs<-1> = FileName:" is not a valid BP file/directory."
    end
  end
return

GetCommitComment:
  * Add spaces, to allow for the "new" comment to be longer than 'CommitComment'. A MR.IN characteristic.
  commitLength = len(CommitComment)
  DisplayComment = CommitComment:spaces(100-commitLength)
  crt "Enter comment:": ; call MR.IN( DisplayComment, '', OUT )
  if OUT = "ESC" or OUT = 'HOME' then stop
  CommitComment = trim(DisplayComment)
return

* =======================================================================================

*  Set all Option flags and set FileList and FileErrs. Note we don't handle the errors
*  now because some options don't require them anyway.

ParseCommandLine:
  CmdLineOptions = "SINGLEPARAM"  ;*  for -M option
  call PARSE.CMDLINE( @SENTENCE, mat CMDLINE, CmdLineOptions )
  if CMD.HELP then gosub Help ; stop
  svnOp = CMD.ARG.S<1, 1>
  FileName = CMD.ARG.S<1, 2>
  locate 'D' in CMD.FLAG.S<1> setting dum then DisplayCmd = @TRUE else DisplayCmd = @FALSE
  locate 'L' in CMD.FLAG.S<1> setting SelectListLoc then
    SelectListName = CMD.PARAM.S<1, SelectListLoc, 1>
  end else SelectListName = ''
  locate 'N' in CMD.FLAG.S<1> setting dum then NoExecute = @TRUE else NoExecute = @FALSE
  locate 'M' in CMD.FLAG.S<1> setting MessagePos then
    CommitComment = CMD.PARAM.S<1, MessagePos, 1>   ;*  'SINGLEPARAM' is set so there's only one line
  end else CommitComment = ''
  locate 'R' in CMD.FLAG.S<1> setting ReleasePos then
    ReleaseNumber = CMD.PARAM.S<1, ReleasePos, 1>
  end else ReleaseNumber = ''
  locate 'V' in CMD.FLAG.S<1> setting dum then Verbose = @TRUE else Verbose = @FALSE
  locate 'P' in CMD.FLAG.S<1> setting PropertyNameAndValueLoc then
    PropertyNameAndValue = CMD.PARAM.S<1, PropertyNameAndValueLoc, 1>
  end else PropertyNameAndValue = ''
  FileList  = ""  ;*  Space separated; each item is a path from /u/dsisrc :
  FileErrs  = ""

  begin case      ;*  Lots of ways to get the FileList:
    case ReleaseNumber  # '' ; gosub ReadRelease
    case SelectListName # '' ;* Use the SELECT list if provided
      getlist SelectListName else Err = "Unable to read Select List ": SelectListName ; return
      loop while readnext ItemID do
        Item = FileName:'/':ItemID ; gosub AddItem
      repeat
    case 1   ;* Get the individual files if no "-L" option was specified
      for N.A = 3 to CMD.NUM.ARGS   ;* We get the files as arguments 3 and more
        Item = FileName:'/':CMD.ARG.S<1,N.A> ; gosub AddItem
      next N.A
  end case

  if svnOp = '' then gosub Help
return

Help:
  crt 'SVN <command> { <BP_Dir> <item> ... | -L <save-list> | -R <release#> } [-N][-M "message"][-V][-D][-P <PropName=PropVal>]'
  crt
  crt " where <command> is COMPILE, COMMIT, ADD, DELETE, DIFF, INFO, LOG, REVERT, STATUS, HELP, PROPSET. Options:"
  crt "  -N : No Execute; for testing just to see the <command>"
  crt "  -V : Verbose for STATUS; default for listed items; NOT default for 'all' option"
  crt "  -D : Display the <command> before executing it."
  crt "  -P : Add more arguments after <command>, useful for PROPSET (example)"
  crt
  crt " For 'COMMIT' when 'M is not used, you will be prompted for a comment if the release has no '**' comment."
  crt
  crt "Examples:"
  crt "  SVN COMMIT DSI.BP AR.FREEZE AR.UNFREEZE"
  crt "  SVN COMMIT DSI.BP -L DSI.BP.FLIST"
  crt "  SVN COMMIT -R 570                    : Commit EVERY program in Release 570 using the '**' line for the Comment"
  crt "  SVN COMPILE -R 570                   : Compile EVERY program in Release 570: NOT REALLY SVN but it's handy!"
  crt "  SVN LISTS   -R 570                   : Save Select Lists for program files: NOT REALLY SVN but it's handy!"
  crt "  SVN STATUS DSI.BP                    : Show all uncommitted 'M'odifications, 'D'eletes, 'A'dds, etc."
  crt "  SVN DIFF DSI.BP AR.FREEZE            : Show the difference between the local copy and what's in SVN"
  crt "  SVN INFO DSI.BP AR.FREEZE PMTS       : Show misc info about what's in SVN"
  crt "  SVN CLEANUP /u/dsisrc/DSI.BP         : Execute cleanup on DSI.BP directory (if something is locked, etc)"
  crt "  SVN LOG DSI.BP AR.FREEZE             : List of historical changes"
  crt
  crt "To add a new file, you need to execute ADD first before Committing (i.e. standard 'svn' procedure):"
  crt "  SVN ADD    DSI.BP NEWFILENAME"
  crt "  SVN COMMIT DSI.BP NEWFILENAME"
  crt
  crt "To delete a file, you need to execute DELETE first, and then 'commit the delete' (again, standard svn):"
  crt "(note: this can be used if you get 'Already Exists' errors ? Just backup your files first !)"
  crt "  SVN DELETE DSI.BP ANYFILENAME"
  crt "  SVN COMMIT DSI.BP ANYFILENAME"
  crt
  crt "To set properties like svn:keywords: "
  crt "  SVN PROPSET DSI.BP ANYFILENAME -P svn:keywords=Id"   
  crt "  SVN PROPSET DSI.BP ANYFILENAME -P svn:keywords=Id Rev Date Author URL"
  crt
  crt "To FIX SVN if you get 'Out of Date' errors (!?) :"
  crt "  SVN UPDATE -R 570                    : UPDATE EVERY program in Release 570 to the current SVN #"
  crt "                                         (CAN THIS OVERWRITE YOUR CHANGES !?)"
  crt
  crt "For more svn options try: svn -help and svn <command> help"
  crt "(see also Confluence page: http://intranet.docmagic.com/display/Development/Universe+SVN+Integration)"
return