SVN

From Pickwiki
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