[1578] | 1 | #!/usr/bin/perl |
---|
| 2 | # ------------------------------------------------------------------------------ |
---|
| 3 | # NAME |
---|
| 4 | # fcm_add_trac.pl |
---|
| 5 | # |
---|
| 6 | # SYNOPSIS |
---|
| 7 | # fcm_add_trac.pl [OPTIONS] <system> |
---|
| 8 | # fcm_add_trac.pl -h |
---|
| 9 | # |
---|
| 10 | # DESCRIPTION |
---|
| 11 | # See $usage for further information. |
---|
| 12 | # |
---|
| 13 | # COPYRIGHT |
---|
| 14 | # (C) Crown copyright Met Office. All rights reserved. |
---|
| 15 | # For further details please refer to the file COPYRIGHT.txt |
---|
| 16 | # which you should have received as part of this distribution. |
---|
| 17 | # ------------------------------------------------------------------------------ |
---|
| 18 | |
---|
| 19 | # Standard pragmas |
---|
| 20 | use strict; |
---|
| 21 | use warnings; |
---|
| 22 | |
---|
| 23 | # Standard modules |
---|
| 24 | use Getopt::Long; |
---|
| 25 | use File::Basename; |
---|
| 26 | use File::Spec; |
---|
| 27 | use Config::IniFiles; |
---|
| 28 | |
---|
| 29 | # In-house modules |
---|
| 30 | use lib File::Spec->catfile ( |
---|
| 31 | dirname ( |
---|
| 32 | (grep {-x File::Spec->catfile ($_, 'fcm')} split (/:/, $ENV{PATH})) [0] |
---|
| 33 | ), |
---|
| 34 | 'lib', |
---|
| 35 | ); |
---|
| 36 | use Fcm::Util; |
---|
| 37 | |
---|
| 38 | # ------------------------------------------------------------------------------ |
---|
| 39 | |
---|
| 40 | # Program information |
---|
| 41 | my $this = basename ($0); |
---|
| 42 | my $year = (localtime)[5] + 1900; |
---|
| 43 | my $copyright = <<EOF; |
---|
| 44 | |
---|
| 45 | (C) Crown copyright $year Met Office. All rights reserved. |
---|
| 46 | EOF |
---|
| 47 | my $usage = <<EOF; |
---|
| 48 | NAME |
---|
| 49 | $this: add a new Trac browser for a FCM project |
---|
| 50 | |
---|
| 51 | SYNOPSIS |
---|
| 52 | $this [OPTIONS] <project> # normal usage |
---|
| 53 | $this [-h] # print help and exit |
---|
| 54 | |
---|
| 55 | DESCRIPTION |
---|
| 56 | This script adds a new Trac browser for a project managed by FCM. The name of |
---|
| 57 | the project is specified in the first argument. |
---|
| 58 | |
---|
| 59 | With the except of --authorised and --help, all options to the script take at |
---|
| 60 | least one argument. If an option can take more than one argument, the |
---|
| 61 | arguments should be specified in a comma-separated list. |
---|
| 62 | |
---|
| 63 | OPTIONS |
---|
| 64 | -a, --authorised - specify an "authorised" permission. Only those users |
---|
| 65 | granted this permission will have "_CREATE" and |
---|
| 66 | "_MODIFY" access. The default is to grant all |
---|
| 67 | authenticated users "_CREATE" and "_MODIFY" access. |
---|
| 68 | |
---|
| 69 | -c, --component - specify one or more components. |
---|
| 70 | |
---|
| 71 | -d, --description - specify a description of the project. |
---|
| 72 | |
---|
| 73 | -e, --smtp-always-cc - specify one or more e-mail addresses, where e-mails |
---|
| 74 | will be sent to on ticket changes. |
---|
| 75 | |
---|
| 76 | -h, --help - prints the usage string and exits. |
---|
| 77 | |
---|
| 78 | -m, --milestone - specify one or more milestones. |
---|
| 79 | |
---|
| 80 | -s, --svn-repos - specify the Subversion repository location. Use an |
---|
| 81 | empty string if no Subversion repository is needed. |
---|
| 82 | |
---|
| 83 | -u, --admin-user - specify one or more admin users. |
---|
| 84 | |
---|
| 85 | -v, --version - specify one or more versions. |
---|
| 86 | $copyright |
---|
| 87 | EOF |
---|
| 88 | |
---|
| 89 | # ------------------------------------------------------------------------------ |
---|
| 90 | |
---|
| 91 | # Options |
---|
| 92 | my ($authorised, $help, $description, $svn_repos); |
---|
| 93 | my (@admin_users, @components, @emails, @milestones, @versions); |
---|
| 94 | |
---|
| 95 | GetOptions ( |
---|
| 96 | 'a|authorised' => \$authorised, |
---|
| 97 | 'c|component=s' => \@components, |
---|
| 98 | 'd|description=s' => \$description, |
---|
| 99 | 'e|smtp-always-cc=s' => \@emails, |
---|
| 100 | 'h|help' => \$help, |
---|
| 101 | 'm|milestone=s' => \@milestones, |
---|
| 102 | 's|svn-repos=s' => \$svn_repos, |
---|
| 103 | 'u|admin-user=s' => \@admin_users, |
---|
| 104 | 'v|version=s' => \@versions, |
---|
| 105 | ); |
---|
| 106 | |
---|
| 107 | @admin_users = split (/,/, join (',', @admin_users)); |
---|
| 108 | @components = split (/,/, join (',', @components)); |
---|
| 109 | @emails = split (/,/, join (',', @emails)); |
---|
| 110 | @milestones = split (/,/, join (',', @milestones)); |
---|
| 111 | @versions = split (/,/, join (',', @versions)); |
---|
| 112 | |
---|
| 113 | if ($help) { |
---|
| 114 | print $usage; |
---|
| 115 | exit; |
---|
| 116 | } |
---|
| 117 | |
---|
| 118 | # Arguments |
---|
| 119 | my $project = $ARGV[0]; |
---|
| 120 | if (not $project) { |
---|
| 121 | print $usage; |
---|
| 122 | exit; |
---|
| 123 | } |
---|
| 124 | |
---|
| 125 | # ------------------------------------------------------------------------------ |
---|
| 126 | |
---|
| 127 | # Useful variables |
---|
| 128 | my $svn_root = '/data/local/fcm/svn/live'; |
---|
| 129 | my $trac_root = File::Spec->catfile ((getpwnam ('fcm'))[7], 'trac', 'live'); |
---|
| 130 | |
---|
| 131 | # Parse the central configuration file |
---|
| 132 | my $central_ini = Config::IniFiles->new ( |
---|
| 133 | '-file' => File::Spec->catfile ($trac_root, 'trac.ini'), |
---|
| 134 | ); |
---|
| 135 | |
---|
| 136 | # ------------------------------------------------------------------------------ |
---|
| 137 | |
---|
| 138 | MAIN: { |
---|
| 139 | # Check for the existence of Trac for the current project |
---|
| 140 | # ---------------------------------------------------------------------------- |
---|
| 141 | my $trac_project = File::Spec->catfile ($trac_root, $project); |
---|
| 142 | die $this, ': ', $trac_project, ': already exists, abort' if -d $trac_project; |
---|
| 143 | |
---|
| 144 | # Check for the existence of SVN for the current project |
---|
| 145 | # ---------------------------------------------------------------------------- |
---|
| 146 | my $svn_project = defined ($svn_repos) |
---|
| 147 | ? $svn_repos |
---|
| 148 | : File::Spec->catfile ($svn_root, $project . '_svn'); |
---|
| 149 | die $this, ': ', $svn_project, ': does not exist, abort' |
---|
| 150 | if $svn_project and not -d $svn_project; |
---|
| 151 | |
---|
| 152 | # Set up "trac-admin" command for this project |
---|
| 153 | # ---------------------------------------------------------------------------- |
---|
| 154 | my @admin_cmd = ('trac-admin', $trac_project); |
---|
| 155 | |
---|
| 156 | # Create project's Trac |
---|
| 157 | my $trac_templates = '/usr/share/trac/templates'; |
---|
| 158 | |
---|
| 159 | my @command = ( |
---|
| 160 | @admin_cmd, |
---|
| 161 | 'initenv', |
---|
| 162 | $project, |
---|
| 163 | 'sqlite:db/trac.db', |
---|
| 164 | ($svn_project ? ('svn', $svn_project) : ('', '')), |
---|
| 165 | $trac_templates, |
---|
| 166 | ); |
---|
| 167 | |
---|
| 168 | &run_command (\@command, PRINT => 1); |
---|
| 169 | |
---|
| 170 | # Ensure the new Trac has the correct group and permissions |
---|
| 171 | &run_command ([qw/chgrp -R apache/, $trac_project], PRINT => 1); |
---|
| 172 | &run_command ([qw/chmod -R g+w/, $trac_project], PRINT => 1); |
---|
| 173 | |
---|
| 174 | # Components |
---|
| 175 | # ---------------------------------------------------------------------------- |
---|
| 176 | my @cur_cmd = (@admin_cmd, 'component'); |
---|
| 177 | |
---|
| 178 | # Remove example components |
---|
| 179 | for my $component (qw/component1 component2/) { |
---|
| 180 | &run_command ([@cur_cmd, 'remove', $component], PRINT => 1); |
---|
| 181 | } |
---|
| 182 | |
---|
| 183 | # Add specified components |
---|
| 184 | for my $component (@components) { |
---|
| 185 | &run_command ([@cur_cmd, 'add', $component, ''], PRINT => 1); |
---|
| 186 | } |
---|
| 187 | |
---|
| 188 | # List components |
---|
| 189 | &run_command ([@cur_cmd, 'list'], PRINT => 1); |
---|
| 190 | |
---|
| 191 | # Versions |
---|
| 192 | # ---------------------------------------------------------------------------- |
---|
| 193 | @cur_cmd = (@admin_cmd, 'version'); |
---|
| 194 | |
---|
| 195 | # Remove example versions |
---|
| 196 | for my $version (qw/1.0 2.0/) { |
---|
| 197 | &run_command ([@cur_cmd, 'remove', $version], PRINT => 1); |
---|
| 198 | } |
---|
| 199 | |
---|
| 200 | # Add specified versions |
---|
| 201 | for my $version (@versions) { |
---|
| 202 | &run_command ([@cur_cmd, 'add', $version], PRINT => 1); |
---|
| 203 | } |
---|
| 204 | |
---|
| 205 | # List versions |
---|
| 206 | &run_command ([@cur_cmd, 'list'], PRINT => 1); |
---|
| 207 | |
---|
| 208 | # Milestones |
---|
| 209 | # ---------------------------------------------------------------------------- |
---|
| 210 | @cur_cmd = (@admin_cmd, 'milestone'); |
---|
| 211 | |
---|
| 212 | # Remove example milestones |
---|
| 213 | for my $milestone (qw/milestone1 milestone2 milestone3 milestone4/) { |
---|
| 214 | &run_command ([@cur_cmd, 'remove', $milestone], PRINT => 1); |
---|
| 215 | } |
---|
| 216 | |
---|
| 217 | # Add specified milestones |
---|
| 218 | for my $milestone (@milestones) { |
---|
| 219 | &run_command ([@cur_cmd, 'add', $milestone], PRINT => 1); |
---|
| 220 | } |
---|
| 221 | |
---|
| 222 | # List milestones |
---|
| 223 | &run_command ([@cur_cmd, 'list'], PRINT => 1); |
---|
| 224 | |
---|
| 225 | # Priority |
---|
| 226 | # ---------------------------------------------------------------------------- |
---|
| 227 | @cur_cmd = (@admin_cmd, 'priority'); |
---|
| 228 | |
---|
| 229 | # Change default priorities |
---|
| 230 | my %priorities = ( |
---|
| 231 | blocker => 'highest', |
---|
| 232 | critical => 'high', |
---|
| 233 | major => 'normal', |
---|
| 234 | minor => 'low', |
---|
| 235 | trivial => 'lowest', |
---|
| 236 | ); |
---|
| 237 | while (my ($old, $new) = each %priorities) { |
---|
| 238 | &run_command ([@cur_cmd, 'change', $old, $new], PRINT => 1); |
---|
| 239 | } |
---|
| 240 | |
---|
| 241 | # List priorities |
---|
| 242 | &run_command ([@cur_cmd, 'list'], PRINT => 1); |
---|
| 243 | |
---|
| 244 | # Severity |
---|
| 245 | # ---------------------------------------------------------------------------- |
---|
| 246 | @cur_cmd = (@admin_cmd, 'severity'); |
---|
| 247 | |
---|
| 248 | # Add serverities |
---|
| 249 | for my $severity (qw/blocker critical major normal minor trivial/) { |
---|
| 250 | &run_command ([@cur_cmd, 'add', $severity], PRINT => 1); |
---|
| 251 | } |
---|
| 252 | |
---|
| 253 | # List serverities |
---|
| 254 | &run_command ([@cur_cmd, 'list'], PRINT => 1); |
---|
| 255 | |
---|
| 256 | # Permission |
---|
| 257 | # ---------------------------------------------------------------------------- |
---|
| 258 | @cur_cmd = (@admin_cmd, 'permission'); |
---|
| 259 | |
---|
| 260 | # Add permissions to admin users |
---|
| 261 | for my $user (@admin_users) { |
---|
| 262 | &run_command ([@cur_cmd, 'add', $user, 'admin'], PRINT => 1); |
---|
| 263 | } |
---|
| 264 | &run_command ([@cur_cmd, qw/add admin TRAC_ADMIN/], PRINT => 1); |
---|
| 265 | |
---|
| 266 | # Remove wiki/ticket create/modify permissions from anonymous users |
---|
| 267 | for my $per (qw/TICKET_CREATE TICKET_MODIFY WIKI_CREATE WIKI_MODIFY/) { |
---|
| 268 | &run_command ([@cur_cmd, qw/remove anonymous/, $per], PRINT => 1); |
---|
| 269 | } |
---|
| 270 | |
---|
| 271 | # Add wiki/ticket create/modify permissions to authenticated/authorised users |
---|
| 272 | for my $per (qw/TICKET_CREATE TICKET_MODIFY WIKI_CREATE WIKI_MODIFY/) { |
---|
| 273 | &run_command ( |
---|
| 274 | [@cur_cmd, 'add', ($authorised ? 'authorised' : 'authenticated'), $per], |
---|
| 275 | PRINT => 1, |
---|
| 276 | ); |
---|
| 277 | } |
---|
| 278 | |
---|
| 279 | # List permissions |
---|
| 280 | &run_command ([@cur_cmd, 'list'], PRINT => 1); |
---|
| 281 | |
---|
| 282 | # Misc modifications to "trac.ini" |
---|
| 283 | # ---------------------------------------------------------------------------- |
---|
| 284 | my $local_file = File::Spec->catfile ($trac_project, 'conf', 'trac.ini'); |
---|
| 285 | |
---|
| 286 | # Read and parse "trac.ini" |
---|
| 287 | my $local_ini = Config::IniFiles->new ('-file' => $local_file); |
---|
| 288 | |
---|
| 289 | # Descriptive name for the current project |
---|
| 290 | my $descr = $description ? $description : $project; |
---|
| 291 | |
---|
| 292 | # Default component |
---|
| 293 | my $def_component = @components ? $components [0] : ''; |
---|
| 294 | |
---|
| 295 | # List of e-mail addresses to always cc |
---|
| 296 | my $smtp_always_cc = @emails ? join (',', @emails) : ''; |
---|
| 297 | |
---|
| 298 | # List of "default" config, in [section, option, value] |
---|
| 299 | my @config = ( |
---|
| 300 | ['notification', 'smtp_always_cc' , $smtp_always_cc], |
---|
| 301 | ['project' , 'descr' , $descr], |
---|
| 302 | ['ticket' , 'default_component' , $def_component], |
---|
| 303 | ); |
---|
| 304 | |
---|
| 305 | for (@config) { |
---|
| 306 | my ($section, $option, $value) = @{ $_ }; |
---|
| 307 | |
---|
| 308 | # Add new section, if necessary |
---|
| 309 | $local_ini->AddSection ($section) |
---|
| 310 | if not $local_ini->SectionExists ($section); |
---|
| 311 | |
---|
| 312 | if (defined ($local_ini->val ($section, $option))) { |
---|
| 313 | # Modify existing option |
---|
| 314 | $local_ini->setval ($section, $option, $value); |
---|
| 315 | |
---|
| 316 | } else { |
---|
| 317 | # Add new option |
---|
| 318 | $local_ini->newval ($section, $option, $value); |
---|
| 319 | } |
---|
| 320 | } |
---|
| 321 | |
---|
| 322 | # Remove duplicated sections/options in the configuration file |
---|
| 323 | for my $section (($local_ini->Sections)) { |
---|
| 324 | # Remove comments |
---|
| 325 | $local_ini->DeleteSectionComment ($section); |
---|
| 326 | |
---|
| 327 | # Leave section in local configuration file if it does not exist in the |
---|
| 328 | # central configuration file |
---|
| 329 | next unless $central_ini->SectionExists ($section); |
---|
| 330 | |
---|
| 331 | for my $parameter (($local_ini->Parameters ($section))) { |
---|
| 332 | # Remove comments |
---|
| 333 | $local_ini->DeleteParameterComment ($section, $parameter); |
---|
| 334 | |
---|
| 335 | # Leave parameter in local configuration file if it does not exist in the |
---|
| 336 | # central configuration file |
---|
| 337 | my $central_value = $central_ini->val ($section, $parameter); |
---|
| 338 | next unless defined $central_value; |
---|
| 339 | |
---|
| 340 | # Remove local parameter if it is the same as the central one |
---|
| 341 | if ($central_value eq $local_ini->val ($section, $parameter)) { |
---|
| 342 | print 'Remove parameter: ', $parameter, ' in section: ', $section, "\n"; |
---|
| 343 | $local_ini->delval ($section, $parameter); |
---|
| 344 | } |
---|
| 345 | } |
---|
| 346 | |
---|
| 347 | # Remove section if it is empty |
---|
| 348 | if (not ($local_ini->Parameters ($section))) { |
---|
| 349 | print 'Remove section: ', $section, "\n"; |
---|
| 350 | $local_ini->DeleteSection ($section); |
---|
| 351 | } |
---|
| 352 | } |
---|
| 353 | |
---|
| 354 | # Rename original "trac.ini" to "trac.ini.orig" |
---|
| 355 | rename $local_file, $local_file . '.orig'; |
---|
| 356 | |
---|
| 357 | # Output modifications to "trac.ini" |
---|
| 358 | $local_ini->WriteConfig ($local_file); |
---|
| 359 | } |
---|
| 360 | |
---|
| 361 | # Finally... |
---|
| 362 | # ------------------------------------------------------------------------------ |
---|
| 363 | print $this, ': finished normally.', "\n"; |
---|
| 364 | exit; |
---|
| 365 | |
---|
| 366 | # ------------------------------------------------------------------------------ |
---|
| 367 | |
---|
| 368 | __END__ |
---|