软件配置管理 HOWTO

1. Overview

CVS 是开放源码的一个奇迹,亦是开放源码得以延续和发展的推动者,是版本控制的经典。CVS 是常用的版本控制工具。

Starteam 是一个集合了版本控制和缺陷控制两种功能的软件,并且具有 CVS 没有的强大的图形界面,易学易用。2002年底被Borland公司收购,发展前景未知,它是我是用的第一个大型商用版本控制软件(真的付了Money的呦)。

提示

评价 CVS、STARTEAM 孰优孰劣,是一个仁者见仁,智者见智的问题。

Starteam 的图形化界面,能够使初学者易于接收,而且其缺陷控制功能的功能(基于数据库的Change Request),是CVS 不具有的,更是相应工具中独树一帜的。

CVS的优秀跨平台能力、强大的命令行、清晰的服务器端数据结构更便于数据的备份、灾难恢复和可靠性。Simple is Butiful 送给 CVS 一定没错。

  1. 侧重不同

    CVS专注于配置管理软件范畴中的版本管理系统。而配置管理系统除了包括版本管理外,还包括构建管理(Build Management)和缺陷跟踪系统。而Starteam包括了这三项。本文档也就只比较其版本管理系统的异同。

  2. 权限控制

    一般来说,CVS的权限设置单一,通常只能通过CVSROOT/passwd, CVSROOT/readers, CVSROOT/writers 文件,同时还要设置CVS REPOS的物理目录权限来完成权限设置,无法完成复杂的权限控制;而STARTEAM无需通过物理路径的权限设置,而是通过自己的数据库管理,实现了类似WINDOWS NT的域用户管理和目录文件ACL控制。

    但是CVS通过CVSROOT目录下的脚本,提供了相应功能扩充的接口,不但可以完成精细的权限控制,还能完成更加个性化的功能。

  3. 易用性

    Starteam的图形界面功能强大,但其命令行功能较CVS弱,需要自己编写shell脚本加强。而随着WINCVS作为图形客户端的可用性的增强,Starteam的强项正在被削弱。

  4. Label的管理

    CVS 的 label 建立在文件上,难以确定到底有多少存在的 label ,而STARTEAM(图形界面)可以很方便知道有哪些 View Label及Revision Label。

    CVS的版本分支, label是建立在文件上,工作在其他分支的程序同样会看到其它分支的label;而Starteam的label只在同一个视图内有效,有独立的命名空间。

    STARTEAM具有Promotion states,可以看作是label的标签。在label上又封装一层的 Promotion State 便于记录版本的提升,和建立自动编译;而CVS 没有浮动 label 的概念,只能通过文件记录方式实现(如我们使用 .promotion 文件记录当前稳定的 label)。

  5. 文件缓存

    Starteam 存在CACHE目录,缓存了所有 Checkout的版本的文件,因此 Checkout速度快。而CVS没有缓存,check out过程较慢,尤其是分支的 Checkout。

    注

    但我对此表示怀疑。尤其是Starteam Unix客户端基于JAVA技术,速度明显比CVS要慢。

  6. 操作文件名和目录名

    CVS的REPOSITORY的物理存储结构即为项目的组织结构,使得文件名修改和目录的重新组织成为难题,往往需要管理员权限去手动操作Repository;而STARTEAM的Repository的文件结构和视图的文件结构可以完全不同,目录、文件名修改和移动非常方便,基于数据库的文件名管理还可以方便地在不同工程之间共享文件,协同工作。

  7. CVS是程序员的工具,STARTEAM还是管理项目的工具

    个人文件管理或小项目的组织,需要轻量级的管理,和灵活的文件导入、导出,以及和自由代码的交互,则CVS,Perforce,PVCS,SourceSafe等面向文件的版本控制占优势。对于公司、大项目,需要独立的测试、QA等分工,还要更多的考虑多样的用户权限控制,易用性(图形界面),缺陷控制,则需要面向工程的STARTEAM来进行版本控制和开发。

  8. CVS的优势

    CVS的优势:项目文件可以方便的组合和移植,这是因为CVS以文件为核心,即面向文件的管理方式。而且目前网络上很多开放源码都采用CVS,可以很好沟通。

    CVS配置简单、健壮、可以很容易地移植;STARTEAM集成度较高,移植过程复杂,需要的管理负担大,需要完善的备份计划。

    CVS是开发源码,服务器端存储易于理解,便于控制;而Starteam为商业软件,服务器端存储方式封闭,难以管理。但是 CVS 不提供图形界面,有较长的学习曲线。

  9. Starteam 的劣势

    不支持分支的合并,需要过多的手动干预。而CVS可以自动完成。

    速度慢,一定程度上影响开发效率。

    不支持并行开发,不能很好解决Merge的问题。而CVS可以智能 Merge。

    故障恢复困难,需要有专职管理员维护。而CVS易于维护。

  10. 评价优劣,不如先用先得

    不用刻意去取舍哪一个,只要用好,即可。况且,已经有了完美的CVS、STARTEAM互相转换的方法。(参见:《CVS, Starteam互相转换》)

下面通过对 CVS、STARTEAM 的服务器端和客户端对照,了解背后的真相。

2. CVS vs. STARTEAM——服务器设置

2.1. CVS服务器设置

2.1.1. 运行 CVS

  • /etc/services

    ...
    cvspserver      2401/tcp
    cvspserver      2401/udp
    ...
  • 用xinetd运行: /etc/xinetd.d/cvs

    service cvspserver
    {
            socket_type     = stream        
            protocol        = tcp
            wait            = no
            user            = root
            server          = /usr/bin/cvs
            server_args     = -f --allow-root=/repos/root1 --allow-root=/repos/root2 pserver
            disable         = no
    }
  • 用inetd运行: /etc/inetd.conf

    ...
    cvspserver stream tcp nowait root /usr/bin/cvs cvs -f --allow-root=/repos/project --allow-root=/repos/user pserver
    ...

2.1.2. 添加帐号和设置权限

  • 创建系统帐号

    假设目录 /repos/project 作为多人共享项目的版本控制根目录,需要用组权限控制;/repos/user作为存放个人独占地版本控制根目录。管理员帐号为cvsroot,项目版本控制的公共帐号为 cvsproject,用户版本控制的公共帐号为 cvsuser,相应的用户组为 cvsroot, cvsproject, cvsuser。

    $ groupadd cvsproject
    $ groupadd cvsuser
    $ groupadd cvsroot
    $ useradd  -g cvsproject -s /sbin/nologin         cvsproject
    $ useradd  -g cvsuser    -s /sbin/nologin         cvsuser
    $ useradd  -g cvsroot    -G cvsproject,cvsuser    cvsroot               (1)
    $ useradd  -g cvsuser    -s /sbin/nologin         cvs_jiangxin          (2)
    $ useradd  -g cvsuser    -s /sbin/nologin         cvs_johnson           
    (1)
    设置 cvsroot 属于多个组,这样 cvsroot 用户除了进行系统维护外(如添加新的工程),还可以和其它组用户一样具有管理代码的权限。
    (2)
    用来和CVS用户帐号一一对应的系统帐号。系统帐号禁止登录,密码设置在相应的 CVS 对应帐号文件中设置。
  • 创建CVS根目录

    $ mkdir -p /repos/project
    $ mkdir -p /repos/user
    $ chown cvsroot:cvsroot /repos/project
    $ chown cvsroot:cvsroot /repos/user
    $ chmod  775 /repos
    $ chmod 2775 /repos/project
    $ chmod 2775 /repos/user
    $ su - cvsroot
    $ cvs -d /repos/project init
    $ cvs -d /repos/user init

    运行完毕 cvs init 之后,在CVS根目录下创建了配置目录CVSROOT,权限如下:

    $ ls -l /repos/project/CVSROOT/
    
    -r--r--r--    1 cvsroot  cvsroot       493 Jan 21 10:37 checkoutlist
    -r--r--r--    1 cvsroot  cvsroot       696 Jan 21 10:37 checkoutlist,v
    -r--r--r--    1 cvsroot  cvsroot       760 Jan 21 10:37 commitinfo
    -r--r--r--    1 cvsroot  cvsroot       963 Jan 21 10:37 commitinfo,v
    -r--r--r--    1 cvsroot  cvsroot       527 Jan 21 10:37 config
    -r--r--r--    1 cvsroot  cvsroot       730 Jan 21 10:37 config,v
    -r--r--r--    1 cvsroot  cvsroot       753 Jan 21 10:37 cvswrappers
    -r--r--r--    1 cvsroot  cvsroot       956 Jan 21 10:37 cvswrappers,v
    -r--r--r--    1 cvsroot  cvsroot      1025 Jan 21 10:37 editinfo
    -r--r--r--    1 cvsroot  cvsroot      1228 Jan 21 10:37 editinfo,v
    drwxrwxr-x    2 cvsroot  cvsroot      4096 Jan 21 10:37 Emptydir
    -rw-rw-rw-    1 cvsroot  cvsroot         0 Jan 21 10:37 history
    -r--r--r--    1 cvsroot  cvsroot      1141 Jan 21 10:37 loginfo
    -r--r--r--    1 cvsroot  cvsroot      1344 Jan 21 10:37 loginfo,v
    -r--r--r--    1 cvsroot  cvsroot      1151 Jan 21 10:37 modules
    -r--r--r--    1 cvsroot  cvsroot      1354 Jan 21 10:37 modules,v
    -r--r--r--    1 cvsroot  cvsroot       564 Jan 21 10:37 notify
    -r--r--r--    1 cvsroot  cvsroot       767 Jan 21 10:37 notify,v
    -r--r--r--    1 cvsroot  cvsroot       649 Jan 21 10:37 rcsinfo
    -r--r--r--    1 cvsroot  cvsroot       852 Jan 21 10:37 rcsinfo,v
    -r--r--r--    1 cvsroot  cvsroot       879 Jan 21 10:37 taginfo
    -r--r--r--    1 cvsroot  cvsroot      1082 Jan 21 10:37 taginfo,v
    -rw-rw-rw-    1 cvsroot  cvsroot         0 Jan 21 10:37 val-tags
    -r--r--r--    1 cvsroot  cvsroot      1026 Jan 21 10:37 verifymsg
    -r--r--r--    1 cvsroot  cvsroot      1229 Jan 21 10:37 verifymsg,v

    可以看出文件 history, val-tags 的权限是任何人可读写,其它文件的权限是任何帐号只读。

    文件 CVSROOT/val-tags 用来确定是否一个TAG是可用的;文件 CVSROOT/history 用来记录CVS的访问记录。

  • 创建 CVS 用户帐号

    使用系统帐号不安全,而CVS提供了独立于系统的用户帐号管理。

    使用配置文件 CVSROOT/passwd, CVSROOT/passwd, CVSROOT/passwd ,来管理帐号。

    $ cat /repos/project/CVSROOT/passwd
    jiangxinroot:_passwd_here_:cvsroot	(1)
    jiangxin:_passwd_here_:cvsproject
    johnson:_passwd_here_:cvsproject	(2)
    anonymous::cvsproject			(3)
    
    $ cat /repos/project/CVSROOT/readers
    anonymous				(4)
    
    (1)
    用户帐号 jiangxinroot,具有和系统帐号 cvsroot 同样权限。
    (2)
    用户帐号 jiangxin, johnson,都具有系统帐号 cvsproject 同样的权限,
    (3)
    匿名帐号 anonymous 的密码为空。
    (4)
    readers 文件中出现的用户帐号,只具有只读权限。

2.1.3. 创建工程

只能以 cvsroot 用户创建工程。因为 cvs 根目录的权限设置为 cvsroot 帐户可写。

  • 在客户端创建工程

    client$ cvs -d :pserver:jiangxinroot@10.0.0.7:/repos/project login
    client$ cvs -d :pserver:jiangxinroot@10.0.0.7:/repos/project import -m "add module test, vendor jiangxin, init_tag start." test jiangxin start
    
    $ ls -l /repos/project
    drwxrwxr-x    3 cvsroot  cvsroot      4096 Jan 21 10:54 CVSROOT
    drwxrwsr-x    2 cvsroot  cvsroot      4096 Jan 21 11:00 test	 (1)
    
    $ chown -R cvsproject:cvsproject test
    $ chmod -R 770 test		
    $ chmod -R g+s test		(2)
    
    client$ cvs -d :pserver:jiangxin@10.0.0.7:/repos/project co test		(3)
    client$ cvs -d :pserver:johnson@10.0.0.7:/repos/project  co -d test2 test
    (1)
    设置工程/模块 test 的目录权限,使只有 cvsproject组用户才有读写权限,其他帐号拒绝访问。
    (3)
    用户帐号 jiangxin, johnson 映射为系统帐号 cvsproject,因此可以访问工程 test。
    (2)
    之所以为模块 test 设置 g+s,是为了当设置一个用户属于多个时,操作不同工程时,能够保障文件的组id维持不变。否则可能影响其它用户的权限。可以用更简单的命令执行 chmod -R 2770 test 进行设置。

    于是创建了多用户共享的工程 test。

  • 另一种创建工程的方法:在服务器端创建工程

    可以直接在服务器端创建目录,设置权限,即完成工程的创建。当然这样创建的工程只是一个空的工程,需要在客户端为空的工程逐个添加文件和目录。

    $ cat /repos/project/CVSROOT/passwd
    jiangxroot:_passwd_here_:cvsroot
    anonymous:_passwd_here_:cvsuser
    jiangxin:_passwd_here_:cvs_jiangxin
    johnson:_passwd_here_:cvs_johnson
    
    $ cd /repos/project
    $ mkdir jiangxin; chown -R cvs_jiangxin:cvsuser jiangxin; chmod -R 2700 jiangxin
    $ mkdir johnson;  chown -R cvs_johnson:cvsuser  johnson;  chmod -R 2700 johnson

    于是创建了两个用户独占的工程。

2.1.4. 用CVS管理文件进行功能扩充

确省安装的CVS的权限仅仅作用于目录,而不能精细到文件级别。而且即使用户只需要拥有文件的只读权限,也要对相应的目录具有写权限,因为需要在目录下创建锁定文件。有一个办法可以避免此问题,即:通过配置文件 CVSROOT/config 的 LockDir 来设置单独的锁定目录,为该单独的锁定目录设置更宽泛的权限控制。

CVS 提供了功能的扩充接口:CVSROOT目录下的管理文件。这些文件提供了相应功能扩充的接口,不但可以完成精细的权限控制,还能完成更加个性化的功能。关于CVSROOT下的脚本,FreeBSD 的源代码就有一个非常好的CVSROOT脚本,可供我们参照:

Setting up a CVS repository - the FreeBSD way。我们可以参照这个指南,定制我们自己的CVSROOT脚本。

下载 FreeBSD 的 CVSROOT 脚本,可以以匿名用户连接到 FreeBSD 的 CVS 服务器:

$ cvs -d :pserver:anoncvs@anoncvs.freebsd.org:2401/home/ncvs login   # 输入密码 anoncvs
$ cvs -d :pserver:anoncvs@anoncvs.freebsd.org:2401/home/ncvs co  CVSROOT-src

下载获得 FreeBSD 的 CVSROOT 代码后,需要进行相应的定制,然后才能 checkin 到自己的 CVSROOT 目录中。这个补丁是我对其的修改和定制:patch.txt

定制过程:

  1. 升级CVS

    确认安装的CVS服务器版本,要高于 1.11.2 。因为我们要用到在版本 1.11.2 才出现的功能:能够在检查commit log 后重新读入 commit log,以实现对 commit log 的格式化。

  2. 定制 PERL 模块 cfg.pm

    文件 CVSROOT/cfg.pm,是 perl脚本的核心包,对其做了一些改动,主要是添加了禁止某些用户发送 Email功能;还增加了部分子过程,部分是从原 log_acum.pl 中移动过来,目的将这些函数设置为模块内部的函数便于其它需要发送邮件的脚本调用,如脚本 log_accum.pl 和 tagcheck 都需要使用这些新增子过程。

    对脚本 CVSROOT/cfg.pm 的改动如下:

    diff -u -r1.1 -r1.2
    --- cfg.pm	14 Aug 2003 10:00:53 -0000	1.1
    +++ cfg.pm	15 Aug 2003 01:44:20 -0000	1.2
    @@ -17,9 +17,10 @@
     	$ADD_TO_LINE $AVAIL_FILE $CHECK_HEADERS $COMMITCHECK_EXTRA
     	@COMMIT_HOSTS $COMMITTER $DEBUG $DIFF_BLOCK_TOTAL_LINES $EXCLUDE_FILE
     	$FILE_PREFIX $IDHEADER $LAST_FILE @LOG_FILE_MAP $MAILADDRS $MAILBANNER
    -	$MAILCMD $MAIL_BRANCH_HDR $MAIL_ON_DIR_CREATION $MAIL_TRANSFORM
    +	$MAILCMD $MAIL_BRANCH_HDR @MAIL_MAP $MAIL_ON_DIR_CREATION $MAIL_TRANSFORM
     	$MINCVSVERSION $MAX_DIFF_SIZE $NO_DOS_LINEBREAKS $PID $PROG_CVS
     	$PROG_MV %TEMPLATE_HEADERS $TMPDIR $UNEXPAND_RCSID $WARN_HEADERS
    +	$BADSENDER_FILE
     );
     
     my $CVSROOT = $ENV{'CVSROOT'} || die "Can't determine /$CVSROOT!";
    @@ -52,7 +53,7 @@
     $PROG_MV =	'/bin/mv';		# mv(1)
     
     # The username of the committer.
    -$COMMITTER = $ENV{"LOGNAME"} || $ENV{'USER'} || getlogin
    +$COMMITTER = $ENV{"CVS_USER"} || $ENV{"LOGNAME"} || $ENV{'USER'} || getlogin   (1)
    		|| (getpwuid($<))[0] || sprintf("uid#%d",$<);
     
     
    @@ -83,6 +84,7 @@
     # commit to what.
     $AVAIL_FILE = "$CVSROOT/CVSROOT/avail";
     
    +$BADSENDER_FILE = "$CVSROOT/CVSROOT/blocksender"; (2)
     
     ################
     ### logcheck ###
    @@ -208,6 +210,10 @@
     	'other'		=> '.*'
     );
     
    +@MAIL_MAP = (
    +	'nobody'	=> '.*'
    +);
    +
     # Include diffs of not greater than this size in kbytes in the
     # commit mail for each file modified. (0 = off).
     $MAX_DIFF_SIZE = 0;
    @@ -270,6 +276,64 @@
     	return @output;
     };
     
    +
    +############################################################
    +#
    +# Subroutines
    +#
    +############################################################
    +
    +# !!! Mailing-list and commitlog history file mappings here !!!
    +# This needs pulling out as a configuration block somewhere so
    +# that others can easily change it.
    +
    +sub get_log_name {
    +	my $dir = shift;	# Directory name
    +
    +
    +	for my $i (0 .. ($#cfg::LOG_FILE_MAP - 1) / 2) {
    +		my $log = $cfg::LOG_FILE_MAP[$i * 2];
    +		my $pattern = $cfg::LOG_FILE_MAP[$i * 2 + 1];
    +
    +		return $log if $dir =~ /$pattern/;
    +	}
    +
    +	return 'other';
    +}
    +
    +sub get_mail_name {
    +	my $dir = shift;	# Directory name
    +	my $CVSROOT = $ENV{'CVSROOT'};
    +	$dir =~ s,^$CVSROOT[/]?,,g;
    +	$dir .= "/" unless $dir =~ ///$/;
    +	
    +	for my $i (0 .. ($#cfg::MAIL_MAP - 1) / 2) {
    +		my $email = $cfg::MAIL_MAP[$i * 2];
    +		my $pattern = $cfg::MAIL_MAP[$i * 2 + 1];
    +		return $email if $dir =~ /$pattern/;
    +	}
    +
    +	return $cfg::MAILADDRS;
    +}
    +
    +
    +# do not send email, if committer is in badsender file...
    +sub sendmail_acl_check {
    +	my $sendmail = 1;
    +	if (-e $cfg::BADSENDER_FILE)
    +	{
    +		open (BADSENDER, $cfg::BADSENDER_FILE) || die "open $cfg::BADSENDER_FILE: $!/n";
    +		while (
                
                
                 
                 ) {
    +			if ($_ =~ //b$cfg::COMMITTER/b/i)
    +			{
    +				$sendmail = 0;
    +				last;
    +			}
    +		}
    +	}
    +	
    +	return $sendmail;
    +}
     
     ######################################################################
     # Load the local configuration file, that allows the entries in this
    
                
                
    (1)
    增加检查环境变量 CVS_USER。以能够正确反映使用 CVSROOT/password 文件进行身份验证的用户名。
    (2)
    通过文件 $BADSENDER_FILE 设置哪些用户对 CVS 操作不必发送邮件,这个功能可以用于自动编译下的特定用户的CVS操作不必发送邮件。

    注

    还在该perl模块中增加了几个过程,供其它程序调用。

    文件 CVSROOT/cfg_local.pm 用于对模块 cfg.pm 进行定制:

    hash$ diff -u -r1.1 cfg_local.pm
    --- cfg_local.pm	14 Aug 2003 10:00:53 -0000	1.1
    +++ cfg_local.pm	15 Aug 2003 03:09:39 -0000	1.3
    @@ -13,7 +13,7 @@
     ####################################################################
     ####################################################################
     
    -$CHECK_HEADERS = 1;
    +$CHECK_HEADERS = 0; (1)
     $IDHEADER = 'FreeBSD';
     $UNEXPAND_RCSID = 1;
     
    @@ -29,25 +29,30 @@
     $MAILCMD = "/usr/local/bin/mailsend -H";
     $MAIL_BRANCH_HDR  = "X-FreeBSD-CVS-Branch";
     $ADD_TO_LINE = 0;
    -$MAILBANNER = "FreeBSD src repository";
    +$MAILBANNER = "My repository"; (2)
     if (defined $ENV{'CVS_COMMIT_ATTRIB'}) {
       my $attrib = $ENV{'CVS_COMMIT_ATTRIB'};
       $MAILBANNER .= " ($attrib committer)";
     }
    +# The minimum version of cvs that we will work with.
    +$MINCVSVERSION = "1110200";  # 1.11.2 (3)
    +
    +$MAIL_ON_DIR_CREATION = 0; (4)
     
     # Sanity check to make sure we've been run through the wrapper and are
     # now primary group 'ncvs'.
     #
    -$COMMITCHECK_EXTRA = sub {
    -	my $GRP=`/usr/bin/id -gn`;
    -	chomp $GRP;
    -	unless ( $GRP =~ /^ncvs$/ ) {
    -		print "You do not have group ncvs (commitcheck)!/n";
    -		exit 1;	# We could return false here.  But there's
    -			# nothing to stop us taking action here instead.
    -	}
    -	return 1;
    -};
    +
    +#$COMMITCHECK_EXTRA = sub { (5)
    +#	my $GRP=`/usr/bin/id -gn`;
    +#	chomp $GRP;
    +#	unless ( $GRP =~ /^ncvs$/ ) {
    +#		print "You do not have group ncvs (commitcheck)!/n";
    +#		exit 1;	# We could return false here.  But there's
    +#			# nothing to stop us taking action here instead.
    +#	}
    +#	return 1;
    +#};
     
     # Wrap this in a hostname check to prevent mail to the FreeBSD
     # list if someone borrows this file and forgets to change it.
    @@ -91,6 +96,22 @@
    @LOG_FILE_MAP = ( (6)
    	'CVSROOT'	=> '^CVSROOT/',
    	'distrib'	=> '^distrib/',
    	'doc'		=> '^doc/',
    	'ports'		=> '^ports/',
    	'www'		=> '^www/',
    
     	'other'		=> '.*'
     );
    +# CVSROOT is still shared between src, ports, doc at the moment. projects has
    +# its own CVSROOT.
    +@MAIL_MAP = ( (7)
    +	'maillist1' => '^CVSROOT/',
    +	'maillist2' => '^src/',
    +	'cvsnone' => '.*',
    +); 
    +
    +@TAG_MAP = ( (8)
    +	'jiangxin' => '^(release|mailstome).*',
    +); 
    +
    +# Email addresses of recipients of commit mail.
    +$MAILADDRS = 'cvsnone'; (9)
    +
    +
     
     1; # Perl requires all modules to return true.  Don't delete!!!!
     #end
    (1)
    设置为0,不强制文件头包含特定的CVS关键字。
    (3)
    设置安装的CVS服务器的最低版本为 1.11.2;
    (4)
    创建目录的事件,也发送邮件。参见脚本: CVSROOT/log_accum.pl
    (5)
    注释该函数,不检查用户组。
    (6)
    定制该数组,将 CVS 模块的 commit log 存储在对应的文件中。
    (7)
    添加哈希表 @MAIL_MAP,设置模块和用户邮件地址的对应关系,相应模块的 commit,通过邮件通知相应用户。
    (8)
    添加哈希表 @TAG_MAP,设置某些格式的TAG只能被某些用户管理。
    (9)
    确省的邮件地址。对于没有在 MAIL_MAP 数组找到匹配的邮件地址,即使用该地址。确省为 'nobody'。
  3. CVS 服务器配置文件:CVSROOT/config

    #SystemAuth=no                 (1)
    #LockDir=/var/lock/cvs         (2)
    #TopLevelAdmin=no
    LogHistory=TOFEWGCMAR          (3)
    RereadLogAfterVerify=always    (4)
    (1)
    如果设置 SystemAuth=no,则只通过 CVS 提供的身份验证。
    (2)
    可以用来指定单独的锁定目录, 便可以更灵活的设置数据仓库的目录权限。
    (3)
    对所有事件记录日志,亦可写为 LogHistory=all。
    (4)
    启用只有在 1.11.2 版本才具有的 commit log 重写功能。
  4. 文件 CVSROOT/cvsignore

    设置版本控制过程中,需要忽略的文件。这些文件将不被显示为未知状态("?")。如:

    *.db
    *.info
    *.[Sp]o
    *.core
    *.aps
    *.clw
    *.exe
    *.ncb
    *.obj
    *.opt
    *.plg
    Debug
    Release

    注

    亦可由每个目录下的文件 .cvsignore 来控制;

    对于 WinCVS,则由文件 C:/.cvsignore 来控制,如果将 HOME 路径设置为 C:/ 的话。

  5. 文件 CVSROOT/cvswrappers

    匹配文件名,并作相应处理。如: -kb 即以二进制方式处理文件。

    *.gif -k 'b'
    *.GIF -k 'b'
    *.jpg -k 'b'
    ...
  6. 文件 CVSROOT/modules

    设置数据仓库中的模块名,可以通过命令:“cvs co -c”察看当前数据仓库(repository)中包含的模块/工程名称。也可以在调整服务器端目录结构时,设置 modules 来保持和以前设置的兼容性。

    CVSROOT	CVSROOT
    module1	module2 &module3
  7. 文件 CVSROOT/checkoutlist

    列在 checkoutlist 中的文件,在 checkin 后,能够自动在服务器 CVSROOT 目录中重建。

    #access             (1)
    avail
    cfg.pm
    cfg_local.pm
    commit_prep.pl
    commitcheck
    cvs_acls.pl
    exclude
    log_accum.pl
    logcheck
    options
    rcstemplate
    tagcheck
    unwrap
    wrap
    (1)
    access 文件供 FreeBSD 的cvs wrapper程序调用,进行权限控制,如不需要该功能注释掉,忽略该文件。
  8. 文件 CVSROOT/notify

    当用户设置了 watch 一个文件,可以定制该文件进行控制。在此该文件未被用到。

  9. 文件 CVSROOT/commitinfo

    Commit 事件要触发三个脚本文件,依次是 commitinfo, verifymsg, loginfo。其中先遍历整个目录树对所有需要 commit 的文件执行 commitinfo文件。再分别针对每一个目录执行 verifymsg, loginfo 脚本。

    commitinfo 会调用 commitcheck 脚本,完成的功能:通过用户主机名、用户名来检查权限;确认CVS服务器的版本号不低于某个版本;将遍历目录树的结果(最后一个目录名)记录下来,以便接下来运行 verifymsg, loginfo的脚本能够确认运行结束等。

    相关文件:CVSROOT/commitcheckCVSROOT/cvs_acls.plCVSROOT/availCVSROOT/commit_prep.plCVSROOT/excludeCVSROOT/cfg.pmCVSROOT/cfg_local.pm

    文件 CVSROOT/avail,被脚本 cvs_acls.pl 读取,再被脚本 commitcheck 调用,用以精细控制权限。例如:

    group|meisters|peter,jdp,markm,joe
    # Pick up the list of bad users from ncvs/CVSROOT/badcommitters  See that
    # file for details
    group|penaltybox|!badcommitters
    
    unavail
    avail||CVSROOT
    avail||distrib
    avail||doc
    avail||ports
    avail||src
    unavail||src/contrib/binutils,src/contrib/file
    avail|obrien|src/contrib/binutils,src/contrib/file
    unavail||src/contrib/tcpdump
    avail|fenner,nectar|src/contrib/tcpdump
    avail||www
    avail|:meisters
    unavail|:penaltybox
  10. 文件 CVSROOT/verifymsg

    用于检查和格式化 commit log。禁止在版本控制提交时,使用空的 commit log。对于 wincvs 在用户不提交 commit log 时,会自动使用“no message”作为commit log。为了禁止该情况发生,需要定制该脚本:

    相关文件:CVSROOT/logcheck

    bash$ diff -u -r1.1 logcheck
    --- logcheck	14 Aug 2003 10:00:53 -0000	1.1
    +++ logcheck	15 Aug 2003 02:01:37 -0000	1.2
    @@ -47,8 +47,10 @@
     
     # Remove leading and trailing blank lines.  (There will be at most
     # one because of the duplicate removal above).
    +local $^W = 0;                        (1)
     shift @log_in if $log_in[0] eq "";
     pop @log_in if $log_in[-1] eq "";
    +local $^W = 1;
     
     # Scan through the commit message looking for templated headers
     # as defined in the configuration file, and rcstemplate.
    @@ -104,6 +106,9 @@
     # completely empty.  This is a bug in cvs.
     my $log = "@log_in";
     die "Log message contains no content!/n" if $log =~ /^/s*$/;
    +
    +# commit without commit log using WINCVS , will automatically provide commit log as "no message".
    +die "Log message contains no content using WINCVS!/n" if $log =~ /^no message$/ or $log =~ /^/.+$/; (2)
    (1)
    隐藏语法警告;
    (2)
    禁止其它的无意义的commit log,其中一个是 WINCVS 缺省的 COMMITLOG,一个是本人以前随意的COMMITLOG风格。
  11. 文件 CVSROOT/loginfo

    将 commit log 分门别类存储在目录 CVSROOT/commitlogs 下,并同时通过邮件外发。为了防止一次事件触发多次的邮件外发,该脚本利用到 commitinfo 的运行结果,只有确认到了目录树的最后,才发送邮件。 模块和存储日志文件以及用户邮件列表在文件CVSROOT/cfg_local.pm中定义。

    相关文件:CVSROOT/log_accum.plCVSROOT/cfg.pm CVSROOT/cfg_local.pm

    文件 CVSROOT/loginfo,调用脚本 log_accum.pl,并传递文件名等参数,用于组织日志和发送邮件。

    原 FreeBSD 脚本存在一个 BUG,如果文件名或者目录名中存在空格,脚本运行不正常,出错,导致不能组织和发送邮件,这个 Bug 修正方法如下:

    在文件 CVSROOT/loginfo中调用 log_accum.pl 时使用的参数由原来的 %s 修改为 %{,,,s},即加入三个连续的逗号作为分隔符,否则使用空格作为分隔符,难以区分是文件的分隔符还是路径或文件名中的空格。

    bash$ tail -1 loginfo
    DEFAULT         $CVSROOT/CVSROOT/log_accum.pl %{,,,s}

    对文件 CVSROOT/log_accum.pl 的修正如下:

    bash$ diff -u -r1.1 log_accum.pl
    
    --- log_accum.pl	14 Aug 2003 10:00:53 -0000	1.1
    +++ log_accum.pl	15 Aug 2003 02:00:35 -0000	1.2
    @@ -47,6 +47,7 @@
     my $LOG_FILE      = "$BASE_FN.log";
     my $SUMMARY_FILE  = "$BASE_FN.summary";
     my $LOGNAMES_FILE = "$BASE_FN.lognames";
    +my $MAILNAMES_FILE = "$BASE_FN.mailnames"; (1)
     my $SUBJ_FILE     = "$BASE_FN.subj";
     my $TAGS_FILE     = "$BASE_FN.tags";
     my $DIFF_FILE     = "$BASE_FN.diff";
    @@ -233,7 +234,8 @@
     	while (
                
                
                 
                 ) {
     		if (/^[ /t]*Repository revision/) {
     			chomp;
    -			my @revline = split;
    +			my @revline = split(/[ /t]+/, $_, 5); 
                 
                 (2)
    +			shift @revline while($revline[0] eq "");
     			$revision = $revline[2];
     			$revline[3] =~ m|^$CVSROOT/+(.*),v$|;
     			$rcsfile = $1;
    @@ -383,20 +385,6 @@
     # !!! Mailing-list and commitlog history file mappings here !!!
     # This needs pulling out as a configuration block somewhere so
     # that others can easily change it.
    -sub get_log_name { 
                 
                 (3)
    -	my $dir = shift;	# Directory name
    -
    -
    -	for my $i (0 .. ($#cfg::LOG_FILE_MAP - 1) / 2) {
    -		my $log = $cfg::LOG_FILE_MAP[$i * 2];
    -		my $pattern = $cfg::LOG_FILE_MAP[$i * 2 + 1];
    -
    -		return $log if $dir =~ /$pattern/;
    -	}
    -
    -	return 'other';
    -}
    -
     
     sub do_changes_file {
     	my @text = @_;
    @@ -408,11 +396,17 @@
     		$unique{$category} = 1;
     
     		my $changes = "$CVSROOT/CVSROOT/commitlogs/$category";
    -		open CHANGES, ">>$changes"
    -			or die "Cannot open $changes./n";
    -		print CHANGES map { "$_/n" } @text;
    -		print CHANGES "/n/n/n";
    -		close CHANGES;
    +		if (open CHANGES, ">>$changes")
    +		{
    +			print CHANGES map { "$_/n" } @text;
    +			print CHANGES "/n/n/n";
    +			close CHANGES;
    +		}
    +		else 
                 
                 (4)
    +		{
    +			print "Cannot open $changes./n";
    +		}
    +
     	}
     }
     
    @@ -420,22 +414,29 @@
     sub mail_notification {
     	my @text = @_;
     
    -# This is turned off since the To: lines go overboard.
    -# Also it has bit-rotted since, and can't just be switched on again.
    -# - but keep it for the time being in case we do something like cvs-stable
    -#	my @mailaddrs = &read_logfile($LOGNAMES_FILE);
    -#	print(MAIL 'To: cvs-committers' . $dom . ", cvs-all" . $dom);
    -#	foreach $line (@mailaddrs) {
    -#		next if ($unique{$line});
    -#		$unique{$line} = 1;
    -#		next if /^cvs-/;
    -#		print(MAIL ", " . $line . $dom);
    -#	}
    -#	print(MAIL "/n");
    +	if (! &cfg::sendmail_acl_check()) 
                 
                 (5)
    +	{
    +		print "mail sent from $cfg::COMMITTER is blocked./n";
    +		return 0;	
    +	}
    +
    +	my %unique = ();
    +	my @mailaddrs = &read_logfile($MAILNAMES_FILE);
    +	# ok, this is kinda hokey, but I need to break up lines with multiple addresses
    +	my $fu = join(" ", @mailaddrs);
    +	@mailaddrs = split " ", $fu;
    +
    +	my $to = "";
    +	foreach my $category (@mailaddrs) {
    +		next if ($unique{$category});
    +		$unique{$category} = 1;
    +
    +		$to .= " " unless $to eq "";
    +		$to .= $category;
    +	}
     
     	my @email = ();
     
    -	my $to = $cfg::MAILADDRS;
     	print "Mailing the commit message to '$to'./n";
     
     	push @email, "To: $to" if $cfg::ADD_TO_LINE;
    @@ -497,10 +498,14 @@
     	}
     
     	# Send the email.
    -	open MAIL, "| $cfg::MAILCMD $to"
    -	    or die "Please check $cfg::MAILCMD.";
    -	print MAIL map { "$_/n" } @email;
    -	close MAIL;
    +	if(fork() == 0)
    +	{
    +		open MAIL, "| $cfg::MAILCMD -F $cfg::COMMITTER $to" 
                 
                 (6)
    +	    		or die "Please check $cfg::MAILCMD.";
    +		print MAIL map { "$_/n" } @email;
    +		close MAIL;
    +	}
    +	exit(0);
     }
     
     
    @@ -634,8 +639,9 @@
     #
     # Initialize basic variables
     #
    +my $separator=",,,"; 
                 
                 (7)
     my $input_params = $ARGV[0];
    -my ($directory, @filenames) = split " ", $input_params;
    +my ($directory, @filenames) = split / ${separator}/, $input_params; 
                 
                 (8)
     #@files = split(' ', $input_params);
     
     my @path = split('/', $directory);
    @@ -660,8 +666,9 @@
     }
     
     # Was used for To: lines, still used for commitlogs naming.
    -&append_line($LOGNAMES_FILE, &get_log_name("$directory/"));
    -&append_line($SUBJ_FILE, "$directory " . join(" ", sort @filenames));
    +&append_line($LOGNAMES_FILE, &cfg::get_log_name("$directory/")); 
                 
                 (9)
    +&append_line($MAILNAMES_FILE, &cfg::get_mail_name("$directory/")); 
                 
                 (10)
    +&append_line($SUBJ_FILE, "$directory/(" . join(",", sort @filenames) .") "); 
                 
                 (11)
     
     #
     # Check for a new directory first.  This will always appear as a
    @@ -697,7 +704,7 @@
     
     	&do_changes_file(@text);
     	&mail_notification(@text);
    -	system("/usr/local/bin/awake", $directory);
    +	# system("/usr/local/bin/awake", $directory);
     	&cleanup_tmpfiles();
     	exit 0;
     }
    @@ -742,7 +749,28 @@
     	}
     
     	# otherwise collect information about which files changed.
    -	my @files = split;
    +	my @tmpfiles = split;
    +	my $strname = "";
    +	my @files;
    +	while (my $item = shift(@tmpfiles))
    +	{
    +		if ($strname eq "") 
    +		{
    +			$strname = $item;
    +		} 
    +		else	
    +		{
    +			$strname .= " $item";
    +		} 
    +		for (my $i=0; $i<=$#filenames; $i++)
    +		{
    +			if ($strname eq $filenames[$i])
    +			{
    +				push (@files, $strname);
    +				$strname = "";
    +			}
    +		}
    +	}
     	push @{ $changed_files{$tag} },	@files if $state == $STATE_CHANGED;
     	push @{ $added_files{$tag} },	@files if $state == $STATE_ADDED;
     	push @{ $removed_files{$tag} },	@files if $state == $STATE_REMOVED;
    @@ -896,7 +924,7 @@
     	&mail_notification(@log_msg);
     }
     
    -system("/usr/local/bin/awake", $directory);
    +# system("/usr/local/bin/awake", $directory);
     &cleanup_tmpfiles();
     exit 0;
     # EOF
                
                
    (1)
    存储邮件地址的临时文件;
    (2)
    避免当文件名中包含空格时,$rcsfile 获取失败;
    (3)
    该函数被移动到 cfg.pm 模块,成为模块的子过程;
    (4)
    没有找到日志记录文件,发出警告,不中断;
    (5)
    读取哈希表 @MAIL_MAP,获取改动代码需要发送到的邮件地址;
    (6)
    发送邮件,设置发件人为 $cfg::COMMITTER;
    (7)
    字段分隔符,应和 loginfo 文件中指定的向匹配!
    (8)
    使用自定义的字段分隔符,以避免由于文件名中出现空格导致文件名获取失败;
    (9)
    该函数已经移动到模块 cfg.pm;
    (10)
    设置收件人名单;
    (11)
    设置邮件标题;
  12. 文件 CVSROOT/taginfo

    在执行 tag/rtag 命令前执行该脚本,如果脚本返回非零值,tag/rtag 动作取消。相关脚本:CVSROOT/tagcheck。负责对添加/删除 TAG 事件进行控制——允许/禁止/发送邮件。

    由于 tag/rtag 事件不象 commit 事件,不是通过多个脚本的配合完成,而是只通过一个脚本 taginfo 完成。这就出现一个问题:如果为一个目录树打上TAG,则可能多次执行脚本,可能要多次触发邮件发送。我的解决办法是,根据TAG进程的 PID 确定在整个过程唯一的文件名,将日志记录到该文件中,taginfo 脚本本身无法知道是否结束,而是系统通过 crontab 定期执行脚本 CVSROOT/checkmailspool.sh来检查是否有完成的 tag 邮件需要外发。

    #!/usr/bin/perl -w
    #
    # Author: Jiang Xin
    # Reference: http://www.worldhello.net/
    #
    
    use strict;
    use lib $ENV{CVSROOT};
    use CVSROOT::cfg;
    
    
    #############################################################
    #
    # Main Body
    #
    # TAG  add/mov/del  repo  files...
    # $1   $2           $3    $4   ...
    #
    ############################################################
    
    my $tag = shift;
    my $action = shift;
    my $repos = shift;
    my $fileitem = "";
    my $filerev= "";
    my $filelist = "";
    
    my $uid = $cfg::COMMITTER;
    my $userlist = "";
    my $pattern = "";
    my $permission = 1;
    my $to = "";
    
    my $tmpstr = &cfg::get_mail_name($repos);
    $tmpstr =~ s//@//./g ;
    $tmpstr="nobody" unless $tmpstr;
    my $MAILFILE = "/var/spool/cvsmail/cvs.tag.$tmpstr.$cfg::PID"; (1)
    
    die "Usage: tagcheck tag action repos files.../n" unless $repos;
    
    for my $i (0 .. ($#cfg::TAG_MAP - 1) / 2) { (2)
    	$userlist = $cfg::TAG_MAP[$i * 2];
    	$pattern = $cfg::TAG_MAP[$i * 2 + 1];
    	
    	if ($tag =~ /$pattern/i)
    	{
    		if ($userlist =~ //b$uid/b/i)
    		{
    			$permission=1;
    			last;
    		}
    		else
    		{
    			$permission=0;
    			last;
    		}
    	}
    }
    
    if ($permission == 0)
    {
    	# normal users can not do this.
    	print STDERR "User /"$cfg::COMMITTER/" canot perform this operation!/n";
    	print STDERR "Only users: $userlist, can handle tag patterm: /"$pattern/"!/n";
    }
    
    while ($fileitem = shift)
    {
            $filerev = shift;
            $filelist = sprintf("%s/t%-24s:/t%s/n", $filelist, $fileitem, $filerev);
    }
    
    print "save message in spool `dirname $MAILFILE`.../n";
    
    my @email = ();
    
    if (! -e $MAILFILE )
    {
    	$to = &cfg::get_mail_name($repos); (3)
    	push @email, "From: $uid<$uid>";
    	push @email, "To: $to";
    	$tmpstr = sprintf("Date: %s", `date -R`);
    	chomp $tmpstr;
    	push @email, $tmpstr;
    
    	if ($permission == 0)
    	{
    		push @email, "Subject: cvs tag FAILED! ($action $tag on $repos)";
    	}
    	else
    	{
    		push @email, "Subject: cvs tag success: $action $tag on $repos";
    	}
    
    	push @email, "";
    
    	delete $ENV{'TZ'};
    	$tmpstr = sprintf("%-11s:    %-8s", "Author", $cfg::COMMITTER);
    	push @email, $tmpstr;
    	$tmpstr = sprintf("%-11s:    %-8s", "Date", `/bin/date +"%Y/%m/%d %H:%M:%S %Z"`);
    	chomp $tmpstr;
    	push @email, $tmpstr;
    	$tmpstr = sprintf("%-11s:    %-8s", "Tag", $tag);
    	push @email, $tmpstr;
    	$tmpstr = sprintf("%-11s:    %-8s", "Operation", $action);
    	push @email, $tmpstr;
    
    	push @email, "";
    	push @email, "  $cfg::MAILBANNER", "" if $cfg::MAILBANNER;
    }
    
    if ($permission == 0)
    {
    	push @email, "Permission denied: $action $tag on $repos !";
    	push @email, "--------------------------------------------------";
    }
    else
    {
    	$tmpstr = sprintf("%-11s:    %-8s", "Repository", $repos);
    	push @email, $tmpstr;
    	push @email, $filelist if $filelist;
    }
    
    #save mail to spool
    open MAIL, ">> $MAILFILE "
    	or die "Cannot open file $MAILFILE for append.";
    print MAIL map { "$_/n" } @email;
    close MAIL;
    
    if ($permission == 0)
    {
    	exit 1
    }
    else
    {
    	exit 0
    }
    
    (1)
    确定进程唯一的文件名称;
    (2)
    @cfg::TAG_MAP 数组定义了需要权限控制的 TAG 名称,以及授权人列表。受限的TAG名称对应于软件开发中的里程碑,要严格的权限控制。和该模式匹配的 tag,只能被授权人操作,其它名称的 TAG,所有用户都可以操作。
    (3)
    邮件地址亦从 MAIL_MAP 数组中获取;

    文件 CVSROOT/checkmailspool.sh,加入到 crontab 中定期执行,检查 tagcheck 生成的邮件。

    #!/bin/sh
    # checkmailspool.sh
    # Auther: Jiang Xin
    #
    # $CVSMAILPATH (cvs mail spool) is a spool directory created by user, 
    # and cvs tag message will store in it.
    # run this script timely to check cvsmail spool and send messages...
    # please put this script in crontab. 
    
    
    CVSMAILPATH=/var/spool/cvsmail
    if [ ! -d $CVSMAILPATH ]; then
    	mkdir -p $CVSMAILPATH
    	chmod 777 $CVSMAILPATH
    fi
    
    cd $CVSMAILPATH
    for i in `ls `; do
    	pid=`echo $i| sed -e "s/.*/./([0-9]*/)$//1/"`
    	
    	xxx=0
    	
    	while ps -p $pid>/dev/null; do
    		xxx=`expr $xxx + 1`
    		if [ $xxx -gt 10 ]; then
    			break
    		fi
    		sleep 3
    		echo waiting $pid, $xxx times ...
    	done
    	echo -e "/n/n========================================" >> $i
    	echo -e "End/n" >> $i
    	cat $i | sendmail -oi -oem -odb -t
    	rm -f $i
    done
  13. 清除CVSROOT产生的临时文件

    以上 CVSROOT 脚步在执行过程中将会在临时目录中产生很多临时文件,如果不加以清理,不但会浪费磁盘空间,更有可能导致发送张冠李戴的错误邮件。在 crontab 中配置每隔一个小时执行一遍以下脚本:

    #/bin/sh
    
    cd /tmp
    ls /#cvs.files.* | sed -e 's//#cvs.files./([0-9]*/)/..*$//1/g' | sort -u | /
    while read xxx; do if ps --pid $xxx>/dev/null 2>&1; then continue; fi ; /
        rm -f /tmp//#cvs.files.$xxx.* ; done

2.2. Starteam服务器权限设置

每一个服务器配置一个独立的用户数据库设置。用户的权限设置灵活,和本机帐号设置无关(不用设置本地帐号),且和本地路径无关。

2.2.1. 用户管理

  1. 如果使能了Lockout功能,则需要建立两个系统管理员帐号,以防止一个被锁定(如多次错误登录被无限期死锁),能用另外一个解锁。

  2. 建组原则(假设组名为 team1)

    • 建立用户组和子组

      假设组名为 team1,首先建立一个大组 ga_team1 组;再在 team1 组下再建立三个child group: g_team1_admin 组,g_team1_users 组和 g_team1_rdonly 组(即只读组)。

    • 建组的考虑

      将权限分配到组,是管理权限的基本原则。因为 starteam 在赋予权限时,是将用户名和组名混在一起,因此为组名加上前缀 “g_” 或者 “ga_”。区分 g_ 和 ga_ 是因为要防止误将权限设置到更大的 ga_ 组中(如后所述)。

      组管理员属于 g_team1_admin 组,拥有管理 LABEL 权限,创建新视图,以及管理代码权限分配权限;starteam管理员部分授权,目的是减少starteam管理员的管理负担。提高效率。

      小组的其它用户属于 g_team1_users 组,除了不具有 g_team1_admin 组的管理权限外,其它权限同 g_team1_admin。

      对于其它有研读代码或者掌握进度需求的用户,如 QA、部门领导等属于 g_team1_rdonly 组,除了具有只读方式看代码权限外,其它同 all users。

      建立项目组 ga_project_manager,是独立于项目组(也可以考虑为每个项目组单独设置),进行项目管理。为管理需求变更,在其下建立 g_pm_req;为管理TODO LIST,建立 g_pm_todo组。

      警告

      分配权限时,要注意不要把权限赋予大组(如 ga_team1 组)。因为 team1 组包括了 g_team1_rdonly 组(只读用户组)!

    • 关于组本身的权限

      除 Administrators 组外,任何组不要设置组的权限,以免组的权限设置超越了项目的安全设置,使对项目的安全设置形同虚设。

  3. 关于发布机使用独立用户

    在UNIX命令行上进行代码的check in/check out,需要建立独立用户,如:team1_release。以免由于使用同一个用户名在不同工作站check代码,造成文件状态UNKNOWS的情况。参见Starteam Howto中关于文件状态unknown的FAQ。

2.2.2. 服务器配置的安全设置

  1. Starteam VTS 的启动方式设置为NT服务方式

    保证服务器重启后,Starteam自动启动。

  2. Starteam 服务器端配置

    • General

      设置登录超时60秒,未动作超时300分钟。

    • Audits

      设置只保留10天安全日志。

    • Vault

      设置不清除文件状态表;

      设置最大CACHE为400MB。

      设置Cache的文件夹在Valut文件夹之外,以使在备份时,对cache区别对待(不备份Cache)。

    • Notifications

      使能Email通知。

    • Protocal

      只保留一种协议支持:TCP/IP(SOCKETS),因为别的协议支持有问题;

      TCP/IP加密级别:不作设置,但是管理员要通知用户,如果客户端支持传输加密,请至少选择RSA R4 stream加密。windows客户端都支持,但是unix命令行不支持。

  3. 访问权限(Access Rights)

    管理员完全权限,不赋予其它用户任何权限。

  4. 系统策略(System Policy)

    • Access Rights

      使能 Ignore object ownership, 即忽略OwnerShip,一切均按照权限设置。

      不选择 Ignore group privileges, 即在保证只有 Administrators 组设置了组权限情况下,允许Administrators组用户超越项目的权限限制实施管理。

    • Security Events

      设置只保留30天的安全日值。

    • Passwords

      设置密钥最小长度6个字符。

    • 在用户管理(User manager)界面,设置所有用户第一次登录必须修改口令,并且不允许设置空口令。

    • Logon Failures

      设置5次失败登录锁定帐号,帐号锁定5分钟。

2.2.3. 工程的权限设置

先打开要配置的工程,Project->Access Rights,来设置工程权限。

  1. Project

    • All Users

      设置所有用户(All Users)具有如下权限:

      See Project and Properties

    • g_xxx_admin

      设置组管理员(如:g_team1_admin 组)具有如下权限:

      赋予除了 Delete project 外所有权限。以便于管理员修改自身权限,给自己加上删除label权限等,但要注意缺省没有赋予管理员的权限,当管理员完成相应管理功能后,恢复原状。

      警告

      不能赋给任何人删除工程权限!因为工程删除不可逆。

  2. Views

    • All Users

      设置所有用户(All Users)具有如下权限:

      See View and its Properties

    • g_xxx_admin

      设置组管理员(如:g_team1_admin 组)具有如下权限:

      赋予除了 Delete View, Delete Labels 外所有权限。

    警告

    Delete View, Delete Labels 不可逆,因此不赋予任何人此权限。但为了减轻管理员负担,赋予了 g_team1_admin 组 change view security setting 权限,g_team1_admin可以修改自己权限,使具有删除 label, 甚至是删除 view 权限,但要注意完成维护功能后,立即去掉该两项权限!

  3. Child Folders

    • g_team1_rdonly 组

      设置只读组(如:g_team1_rdonly 组)具有如下权限:

      具有:See folder and its properties, See folder history, See folder links 权限。

    • g_team1_users 组

      设置普通用户组(g_team1_users 组)具有如下权限:

      除 Modify folder properties, Delete folder from parent folder, Change folder security set 外所有权限。

    • g_team1_admin组

      设置组管理员(如:g_team1_admin 组)具有如下权限:

      除了 Delete folder from parent folder 外所有权限。

  4. Files

    • g_team1_rdonly 组

      设置只读组(如:g_team1_rdonly 组)具有如下权限:

      具有:See file and its properties, See file history, See file links, Check out files 权限。

    • g_team1_users 组

      设置普通用户组(g_team1_users 组)具有如下权限:

      除 Change file security set 外所有权限。

    • g_team1_admin 组

      设置组管理员(如:g_team1_admin 组)具有如下权限:

      所有权限。

      警告

      Move folder out of a view or project 权限也是危险操作,文件移出视图,也从历史视图中去掉,可能会破坏完整性。但将文件或目录移动到另外一个视图,是从服务器端删除文件的唯一办法。因此慎用。

  5. Change Request

    • All Users

      添加所有用户(All Users),将权限设置为空。即默认禁止一切的策略。

    注

    工程的权限设置中的ChildFolders, Files, Change request 等的权限设置,用来确定工程的默认设置。即为没有对各项进行单独权限设置时的默认安全设置。

    将 Change Request 定义为默认禁止一切,目的是只在指定目录添加 Change Request.

  6. Topics

    • All Users

      设置所有用户(All Users)具有如下权限:

      设置除了 “Change topic security set”,“Delete Topic from folder”外所有权限。提供用户通过 TOPIC 进行讨论以及保存备忘。

    • 设置组管理员(如:g_team1_admin 组)具有所有权限。

2.2.4. 视图的权限设置

先打开要配置的视图,View->Access Rights,来设置视图权限。

  1. 根视图

    可以不设置权限,继承整个工程中的Project AccessRight的 Views 的各项权限设置。

  2. 分支视图

    按需设置。可以使老版本的维护由其他人完成,或者进行 xp 模式开发,为开发者(非管理员)设置更大的权限。

2.2.5. 文件夹的权限设置

选择视图下相应文件夹,鼠标右键单击,选择 Advanced->Access Rights,设置权限。

需要先设置“根目录”权限。再单独设置各一级子目录(defect, src, doc等)的权限。如果需要还可以对二级目录等(模块)设置权限,但不建议设置二级目录的权限。

  1. 根目录

    根目录和子目录在权限设置上的异同

    1. 根目录的权限不传递,即在根目录上设置的权限对派生的子视图没有影响;而非根目录会传递权限,所以尽量不要在子目录(非根目录)上设置权限,以免连带影响其它视图中该目录的权限,造成混乱。

    2. 根目录继承视图或者工程的 Child Folder 权限;子目录先继承根目录权限,再继承视图,工程权限。

    3. 因此,不要设置根目录的权限页的 Child Folder 页,这样没有设置权限的子目录便可以继承视图的 Child Folder页的权限。但是可能需要设置根目录权限页的 This Folder 页,因为 This Folder的权限默认取自视图或者工程的 Child Folder 设置。如果有子目录的权限大于视图或者工程的 Child Folder的设置,就需要设置This Folder 设置。但是副作用是,子目录的权限并没有完全继承视图或者工程的更严格的设置,而是采用了根目录的 This Folder 设置和 视图的Subfolder设置的组合(因为根目录没有设置Subfolder权限)。这样有可能使所有能够访问根目录的用户访问整个目录树!!!好像没有更好的解决方案。

    根目录上的权限设置:

    • This Folder

      All Users:所有用户(All Users)具有 See folder and its properties, See folder history, See folder links 权限。

      g_team1_admin: 组管理员(g_team1_admin)具有:除了Delete folder from parent folder 外所有权限。

    • Child Folders,Files, Change Requests, Tasks, Topics

      不作设置,以继承视图或者工程的权限设置。

  2. Defect目录

    • This Folder

      All Users : 所有用户(All Users)具有—— See folder and its properties, See folder history, See folder links 权限。

      g_qa_admin:QA组管理员(g_qa_admin)具有——除了Delete folder from parent folder 外所有权限。

      g_team1_admin:team1组管理员(g_team1_admin)具有——除了Delete folder from parent folder 外所有权限。

      注

      设置服务QA管理员和team1目录管理权限,允许QA管理员建立子目录,用于对CR分类。

    • Child Folders

      All Users:所有用户(All Users)具有——See folder and its properties, See folder history, See folder links 权限。

      g_qa_admin:QA组管理员(g_qa_admin)具有——除了Delete folder from parent folder 外所有权限。

      g_team1_admin:team1组管理员(g_team1_admin)具有——除了Delete folder from parent folder 外所有权限。

    • Change Requests

      All Users:所有用户(All Users)具有——除了Change CR Security set , Delete CR from folder外所有权限。

      g_qa_admin:QA组管理员(g_qa_admin)具有——除了Delete CR from folder 外所有权限。

      警告

      为了减轻管理员负担,赋予了 g_qa_admin 组 change CR security setting 权限,g_qa_admin可以修改change request的权限,使之可以被删除,再删除该 cr。

    • Files, Tasks, Topics

      不另外设置权限,继承视图或者工程的权限。

  3. src 目录

    src 目录是用来存放源代码的目录,应着重注意本目录权限设置。

    可以不作任何单独设置,因为继承了视图或者工程的权限设置。如果工程或者视图关于文件的权限设置足够安全的话,可以不必设置本目录权限。

  4. ProjectManagement 目录

    ProjectManagement 目录,用于以文件和 TOPIC形式存储 TODO_LIST, 编程规范,需求变更。

    1. ProjectManagement 目录

      • This Folder

        All Users :所有用户具有—— See folder and its properties, See folder history, See folder links 权限。

        ga_project_manager 组 :具有——除 Modify folder properties, Delete folder from parent folder, Change folder security set 外所有权限。

        g_team1_admin组 :具有——除 Delete folder from parent folder 外所有权限。

      • Child Folders

        All Users :所有用户具有—— See folder and its properties, See folder history, See folder links 权限。

        ga_project_manager 组 :具有——除 Modify folder properties, Delete folder from parent folder, Change folder security set 外所有权限。

        g_team1_admin组 :具有——除 Delete folder from parent folder 外所有权限。

      • Files

        All Users:具有——See file and its properties, See file history, See file links, Check out files 权限。

        ga_project_manager:普通用户组具有——除了 Change file security set, Delete file from folder 外的所有权限。

        g_team1_admin:组管理员具有——除了 Delete file from folder 外的所有权限。

      • Topic

        All Users:具有——除了 Change top security set, Delete topic from forder 外所有权限。

        g_team1_admin:具有——除了 Delete topic from forder 外所有权限。

      • Change

        暂不设置,采用工程缺省禁用配置。

    2. TODO LIST子目录

      所有用户具有只读文件,读写TOPIC权限;g_pm_todo 组 和 g_team1_admin组具有读写文件,读写topic,和整理目录权限。

    3. 编程规范

      所有用户都有整理目录,读写topic权限;g_team1_admin 多了一项管理文件的权限。

    4. 需求变更

      所有用户具有只读文件,只读TOPIC权限;g_pm_req 组 和 g_team1_admin 组具有读写文件,读写topic,和整理目录权限。

2.2.6. Starteam服务器优化

2.2.6.1. 提高数据库性能

  • 数据库可以有多种选择,但是ACCESS基本可以满足需求,虽然有1.2G文件大小限制,但一般应用不会超过。数据库的选择不是性能瓶颈。

  • 安装和使用 Copy Indexes Tools in "StarTeam VirtualTeam Server 4.0/Tuning Scripts/MS Access/Package"。

  • 压缩数据库;(ODBCàsystem dsn)

  • 增加数据库的Max Buffer size从2048至8192,Threads 从3至 24。

  • 关于其他数据库,参见 http://devforum.starbase.com/starbase/Main.asp

2.2.6.2. 提高Starteam服务器性能

  • 增加Cache目录文件大小至400M;

  • 将文件状态表导出时间从180天减少到30天;

  • 将审计导出时间从不导出更改为30天导出;

  • 在客户端设置Allow check out of deltas "Tools | Workstation | Files "Optimize for slow connection.""

  • 参见http://devforum.starbase.com/starbase/Articles/Default.asp?id=206

2.2.6.3. 备份

备份涉及到:

  • The Configuration and cipher files

    Configuration/server.scg

    Configuration/server.cph

  • Database files

    Database/StarTeamDB.mdb (Assuming Access database)

  • Vault:

    Archive dir: *.*

    Attachments dir: *.*

  • Optional

    Cache dir: *.*

参见http://devforum.starbase.com/starbase/Articles/Default.asp?id=133

3. CVS vs. STARTEAM——服务器端存储

3.1. CVS 服务器端存储

$ pwd
/repos/local

$ ls -l
total 0
drwxr-xr-x    3 Administ None            0 Dec 14 20:59 CVSROOT
drwxr-xr-x   12 Administ None            0 Dec 29 21:47 worldhello

$ ls CVSROOT   
Emptydir        commitinfo,v    cvswrappers,v   loginfo         notify          rcsinfo,v       val-tags
checkoutlist    config          editinfo        loginfo,v       notify,v        readers         verifymsg
checkoutlist,v  config,v        editinfo,v      modules         passwd          taginfo         verifymsg,v
commitinfo      cvswrappers     history         modules,v       rcsinfo         taginfo,v

$ cd worldhello

$ ls -F
bio/     docbook/   html/    inc/          life/   resource/  utility/
daily,v  favorite/  images/  index.html,v  make,v  sports/

$ head index.html,v
head     1.1;
branch   1.1.1;
access   ;
symbols  start:1.1.1.1 jiangxin:1.1.1;
locks    ; strict;
comment  @# @;


1.1
date     2002.12.29.13.47.00;  author jiangxin;  state Exp;

可以看出CVS的服务器端存储和客户端的是一一对应的,树形的文件系统。

3.2. Starteam 服务器端存储

注

以下的示意,是在 Win2k 平台,在 cygwin 命令行下输出

Starteam 的数据库

$ pwd
/d/.repos/STARTEAM/johnson/Database

$ ls -l
-rw-r--r--    1 Administ None      2101248 Sep  5 20:22 StarTeamDB.mdb

版本控制文件

$ pwd
/d/.repos/STARTEAM/johnson/Vault/Archive

$ ls -F
00000000  00000006  0000000C  00000012  00000018  0000001E  00000024  0000002A
00000001  00000007  0000000D  00000013  00000019  0000001F  00000025  0000002B
00000002  00000008  0000000E  00000014  0000001A  00000020  00000026
00000003  00000009  0000000F  00000015  0000001B  00000021  00000027
00000004  0000000A  00000010  00000016  0000001C  00000022  00000028
00000005  0000000B  00000011  00000017  0000001D  00000023  00000029

缓存

$ pwd
/d/.repos/STARTEAM/CACHE/johnson

$ ls -F
00000000.1  00000001.3  00000007.1  00000011.1  0000001B.1  00000025.1
00000000.2  00000001.4  00000008.1  00000012.1  0000001C.1  00000026.1
00000000.3  00000001.5  00000009.1  00000013.1  0000001D.1  00000027.1
00000000.4  00000001.6  0000000A.1  00000014.1  0000001E.1  00000028.1
00000000.5  00000002.1  0000000B.1  00000015.1  0000001F.1  00000029.1
00000000.6  00000002.2  0000000C.1  00000016.1  00000020.1  0000002A.1
00000000.7  00000003.1  0000000D.1  00000017.1  00000021.1  0000002B.1
00000000.8  00000004.1  0000000E.1  00000018.1  00000022.1
00000001.1  00000005.1  0000000F.1  00000019.1  00000023.1
00000001.2  00000006.1  00000010.1  0000001A.1  00000024.1

可以看出Starteam的服务器端存储和客户端完全不同,服务器端文件存储结构是平面结构。文件名和实际物理存储的对应是通过数据库来实现的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值