<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="generator" content="Asciidoctor 2.0.11"> <meta name="author" content="Perforce Professional Services"> <title>Server Deployment Package (SDP) for Perforce Helix: Unsupported Scripts and Triggers</title> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"> <style> /* Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */ /* Uncomment @import statement to use as custom stylesheet */ /*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section{display:block} audio,video{display:inline-block} audio:not([controls]){display:none;height:0} html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%} a{background:none} a:focus{outline:thin dotted} a:active,a:hover{outline:0} h1{font-size:2em;margin:.67em 0} abbr[title]{border-bottom:1px dotted} b,strong{font-weight:bold} dfn{font-style:italic} hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0} mark{background:#ff0;color:#000} code,kbd,pre,samp{font-family:monospace;font-size:1em} pre{white-space:pre-wrap} q{quotes:"\201C" "\201D" "\2018" "\2019"} small{font-size:80%} sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} sup{top:-.5em} sub{bottom:-.25em} img{border:0} svg:not(:root){overflow:hidden} figure{margin:0} fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em} legend{border:0;padding:0} button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} button,input{line-height:normal} button,select{text-transform:none} button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer} button[disabled],html input[disabled]{cursor:default} input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0} button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} textarea{overflow:auto;vertical-align:top} table{border-collapse:collapse;border-spacing:0} *,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} html,body{font-size:100%} body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased} a:hover{cursor:pointer} img,object,embed{max-width:100%;height:auto} object,embed{height:100%} img{-ms-interpolation-mode:bicubic} .left{float:left!important} .right{float:right!important} .text-left{text-align:left!important} .text-right{text-align:right!important} .text-center{text-align:center!important} .text-justify{text-align:justify!important} .hide{display:none} img,object,svg{display:inline-block;vertical-align:middle} textarea{height:auto;min-height:50px} select{width:100%} .subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em} div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0} a{color:#2156a5;text-decoration:underline;line-height:inherit} a:hover,a:focus{color:#1d4b8f} a img{border:0} p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility} p aside{font-size:.875em;line-height:1.35;font-style:italic} h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em} h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0} h1{font-size:2.125em} h2{font-size:1.6875em} h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em} h4,h5{font-size:1.125em} h6{font-size:1em} hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0} em,i{font-style:italic;line-height:inherit} strong,b{font-weight:bold;line-height:inherit} small{font-size:60%;line-height:inherit} code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)} ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} ul,ol{margin-left:1.5em} ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em} ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit} ul.square{list-style-type:square} ul.circle{list-style-type:circle} ul.disc{list-style-type:disc} ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} dl dt{margin-bottom:.3125em;font-weight:bold} dl dd{margin-bottom:1.25em} abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help} abbr{text-transform:none} blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)} blockquote cite::before{content:"\2014 \0020"} blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)} blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)} @media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2} h1{font-size:2.75em} h2{font-size:2.3125em} h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em} h4{font-size:1.4375em}} table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede} table thead,table tfoot{background:#f7f8f7} table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left} table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)} table tr.even,table tr.alt{background:#f8f8f7} table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6} h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em} h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400} .center{margin-left:auto;margin-right:auto} .stretch{width:100%} .clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table} .clearfix::after,.float-group::after{clear:both} :not(pre).nobreak{word-wrap:normal} :not(pre).nowrap{white-space:nowrap} :not(pre).pre-wrap{white-space:pre-wrap} :not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed} pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed} pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit} pre>code{display:block} pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal} em em{font-style:normal} strong strong{font-weight:400} .keyseq{color:rgba(51,51,51,.8)} kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap} .keyseq kbd:first-child{margin-left:0} .keyseq kbd:last-child{margin-right:0} .menuseq,.menuref{color:#000} .menuseq b:not(.caret),.menuref{font-weight:inherit} .menuseq{word-spacing:-.02em} .menuseq b.caret{font-size:1.25em;line-height:.8} .menuseq i.caret{font-weight:bold;text-align:center;width:.45em} b.button::before,b.button::after{position:relative;top:-1px;font-weight:400} b.button::before{content:"[";padding:0 3px 0 2px} b.button::after{content:"]";padding:0 2px 0 3px} p a>code:hover{color:rgba(0,0,0,.9)} #header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em} #header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table} #header::after,#content::after,#footnotes::after,#footer::after{clear:both} #content{margin-top:1.25em} #content::before{content:none} #header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} #header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf} #header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px} #header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap} #header .details span:first-child{margin-left:-.125em} #header .details span.email a{color:rgba(0,0,0,.85)} #header .details br{display:none} #header .details br+span::before{content:"\00a0\2013\00a0"} #header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)} #header .details br+span#revremark::before{content:"\00a0|\00a0"} #header #revnumber{text-transform:capitalize} #header #revnumber::after{content:"\00a0"} #content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem} #toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em} #toc>ul{margin-left:.125em} #toc ul.sectlevel0>li>a{font-style:italic} #toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0} #toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none} #toc li{line-height:1.3334;margin-top:.3334em} #toc a{text-decoration:none} #toc a:active{text-decoration:underline} #toctitle{color:#7a2518;font-size:1.2em} @media screen and (min-width:768px){#toctitle{font-size:1.375em} body.toc2{padding-left:15em;padding-right:0} #toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} #toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em} #toc.toc2>ul{font-size:.9em;margin-bottom:0} #toc.toc2 ul ul{margin-left:0;padding-left:1em} #toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em} body.toc2.toc-right{padding-left:0;padding-right:15em} body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}} @media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0} #toc.toc2{width:20em} #toc.toc2 #toctitle{font-size:1.375em} #toc.toc2>ul{font-size:.95em} #toc.toc2 ul ul{padding-left:1.25em} body.toc2.toc-right{padding-left:0;padding-right:20em}} #content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px} #content #toc>:first-child{margin-top:0} #content #toc>:last-child{margin-bottom:0} #footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em} #footer-text{color:rgba(255,255,255,.8);line-height:1.44} #content{margin-bottom:.625em} .sect1{padding-bottom:.625em} @media screen and (min-width:768px){#content{margin-bottom:1.25em} .sect1{padding-bottom:1.25em}} .sect1:last-child{padding-bottom:0} .sect1+.sect1{border-top:1px solid #e7e7e9} #content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400} #content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em} #content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible} #content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none} #content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221} details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em} details>summary:first-of-type{cursor:pointer;display:list-item;outline:none;margin-bottom:.75em} .admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic} table.tableblock.fit-content>caption.title{white-space:nowrap;width:0} .paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)} table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit} .admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%} .admonitionblock>table td.icon{text-align:center;width:80px} .admonitionblock>table td.icon img{max-width:none} .admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase} .admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6)} .admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} .exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px} .exampleblock>.content>:first-child{margin-top:0} .exampleblock>.content>:last-child{margin-bottom:0} .sidebarblock{border-style:solid;border-width:1px;border-color:#dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;-webkit-border-radius:4px;border-radius:4px} .sidebarblock>:first-child{margin-top:0} .sidebarblock>:last-child{margin-bottom:0} .sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} .exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0} .literalblock pre,.listingblock>.content>pre{-webkit-border-radius:4px;border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em} @media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}} @media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}} .literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class="highlight"],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8} .literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)} .listingblock>.content{position:relative} .listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5} .listingblock:hover code[data-lang]::before{display:block} .listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5} .listingblock.terminal pre .command:not([data-prompt])::before{content:"$"} .listingblock pre.highlightjs{padding:0} .listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px} .listingblock pre.prettyprint{border-width:0} .prettyprint{background:#f7f7f8} pre.prettyprint .linenums{line-height:1.45;margin-left:2em} pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0} pre.prettyprint li code[data-lang]::before{opacity:1} pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none} table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none} table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal} table.linenotable td.code{padding-left:.75em} table.linenotable td.linenos{border-right:1px solid currentColor;opacity:.35;padding-right:.5em} pre.pygments .lineno{border-right:1px solid currentColor;opacity:.35;display:inline-block;margin-right:.75em} pre.pygments .lineno::before{content:"";margin-right:-.125em} .quoteblock{margin:0 1em 1.25em 1.5em;display:table} .quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em} .quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify} .quoteblock blockquote{margin:0;padding:0;border:0} .quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)} .quoteblock blockquote>.paragraph:last-child p{margin-bottom:0} .quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right} .verseblock{margin:0 1em 1.25em} .verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility} .verseblock pre strong{font-weight:400} .verseblock .attribution{margin-top:1.25rem;margin-left:.5ex} .quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic} .quoteblock .attribution br,.verseblock .attribution br{display:none} .quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)} .quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none} .quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0} .quoteblock.abstract{margin:0 1em 1.25em;display:block} .quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center} .quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf} .quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0} .quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem} .quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;text-align:left;margin-right:0} table.tableblock{max-width:100%} p.tableblock:last-child{margin-bottom:0} td.tableblock>.content{margin-bottom:1.25em} td.tableblock>.content>:last-child{margin-bottom:-1.25em} table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede} table.grid-all>*>tr>*{border-width:1px} table.grid-cols>*>tr>*{border-width:0 1px} table.grid-rows>*>tr>*{border-width:1px 0} table.frame-all{border-width:1px} table.frame-ends{border-width:1px 0} table.frame-sides{border-width:0 1px} table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0} table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0} table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0} table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0} table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd),table.stripes-even tr:nth-of-type(even),table.stripes-hover tr:hover{background:#f8f8f7} th.halign-left,td.halign-left{text-align:left} th.halign-right,td.halign-right{text-align:right} th.halign-center,td.halign-center{text-align:center} th.valign-top,td.valign-top{vertical-align:top} th.valign-bottom,td.valign-bottom{vertical-align:bottom} th.valign-middle,td.valign-middle{vertical-align:middle} table thead th,table tfoot th{font-weight:bold} tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7} tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold} p.tableblock>code:only-child{background:none;padding:0} p.tableblock{font-size:1em} ol{margin-left:1.75em} ul li ol{margin-left:1.5em} dl dd{margin-left:1.125em} dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0} ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em} ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none} ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em} ul.unstyled,ol.unstyled{margin-left:0} ul.checklist{margin-left:.625em} ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em} ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em} ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em} ul.inline>li{margin-left:1.25em} .unstyled dl dt{font-weight:400;font-style:normal} ol.arabic{list-style-type:decimal} ol.decimal{list-style-type:decimal-leading-zero} ol.loweralpha{list-style-type:lower-alpha} ol.upperalpha{list-style-type:upper-alpha} ol.lowerroman{list-style-type:lower-roman} ol.upperroman{list-style-type:upper-roman} ol.lowergreek{list-style-type:lower-greek} .hdlist>table,.colist>table{border:0;background:none} .hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none} td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em} td.hdlist1{font-weight:bold;padding-bottom:1.25em} .literalblock+.colist,.listingblock+.colist{margin-top:-.5em} .colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top} .colist td:not([class]):first-child img{max-width:none} .colist td:not([class]):last-child{padding:.25em 0} .thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd} .imageblock.left{margin:.25em .625em 1.25em 0} .imageblock.right{margin:.25em 0 1.25em .625em} .imageblock>.title{margin-bottom:0} .imageblock.thumb,.imageblock.th{border-width:6px} .imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em} .image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0} .image.left{margin-right:.625em} .image.right{margin-left:.625em} a.image{text-decoration:none;display:inline-block} a.image object{pointer-events:none} sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super} sup.footnote a,sup.footnoteref a{text-decoration:none} sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline} #footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em} #footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0} #footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em} #footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em} #footnotes .footnote:last-of-type{margin-bottom:0} #content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0} .gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0} .gist .file-data>table td.line-data{width:99%} div.unbreakable{page-break-inside:avoid} .big{font-size:larger} .small{font-size:smaller} .underline{text-decoration:underline} .overline{text-decoration:overline} .line-through{text-decoration:line-through} .aqua{color:#00bfbf} .aqua-background{background:#00fafa} .black{color:#000} .black-background{background:#000} .blue{color:#0000bf} .blue-background{background:#0000fa} .fuchsia{color:#bf00bf} .fuchsia-background{background:#fa00fa} .gray{color:#606060} .gray-background{background:#7d7d7d} .green{color:#006000} .green-background{background:#007d00} .lime{color:#00bf00} .lime-background{background:#00fa00} .maroon{color:#600000} .maroon-background{background:#7d0000} .navy{color:#000060} .navy-background{background:#00007d} .olive{color:#606000} .olive-background{background:#7d7d00} .purple{color:#600060} .purple-background{background:#7d007d} .red{color:#bf0000} .red-background{background:#fa0000} .silver{color:#909090} .silver-background{background:#bcbcbc} .teal{color:#006060} .teal-background{background:#007d7d} .white{color:#bfbfbf} .white-background{background:#fafafa} .yellow{color:#bfbf00} .yellow-background{background:#fafa00} span.icon>.fa{cursor:default} a span.icon>.fa{cursor:inherit} .admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default} .admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c} .admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111} .admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900} .admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400} .admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000} .conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);-webkit-border-radius:50%;border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold} .conum[data-value] *{color:#fff!important} .conum[data-value]+b{display:none} .conum[data-value]::after{content:attr(data-value)} pre .conum[data-value]{position:relative;top:-.125em} b.conum *{color:inherit!important} .conum:not([data-value]):empty{display:none} dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility} h1,h2,p,td.content,span.alt{letter-spacing:-.01em} p strong,td.content strong,div.footnote strong{letter-spacing:-.005em} p,blockquote,dt,td.content,span.alt{font-size:1.0625rem} p{margin-bottom:1.25rem} .sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em} .exampleblock>.content{background:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc} .print-only{display:none!important} @page{margin:1.25cm .75cm} @media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important} html{font-size:80%} a{color:inherit!important;text-decoration:underline!important} a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important} a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em} abbr[title]::after{content:" (" attr(title) ")"} pre,blockquote,tr,img,object,svg{page-break-inside:avoid} thead{display:table-header-group} svg{max-width:100%} p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3} h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid} #header,#content,#footnotes,#footer{max-width:none} #toc,.sidebarblock,.exampleblock>.content{background:none!important} #toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important} body.book #header{text-align:center} body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em} body.book #header .details{border:0!important;display:block;padding:0!important} body.book #header .details span:first-child{margin-left:0!important} body.book #header .details br{display:block} body.book #header .details br+span::before{content:none!important} body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important} body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always} .listingblock code[data-lang]::before{display:block} #footer{padding:0 .9375em} .hide-on-print{display:none!important} .print-only{display:block!important} .hide-for-print{display:none!important} .show-for-print{display:inherit!important}} @media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem} .sect1{padding:0!important} .sect1+.sect1{border:0} #footer{background:none} #footer-text{color:rgba(0,0,0,.6);font-size:.9em}} @media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}} </style> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"> </head> <body class="book"> <div id="header"> <h1>Server Deployment Package (SDP) for Perforce Helix: Unsupported Scripts and Triggers</h1> <div class="details"> <span id="author" class="author">Perforce Professional Services</span><br> <span id="email" class="email"><a href="mailto:consulting@perforce.com">consulting@perforce.com</a></span><br> <span id="revnumber">version v2019.3,</span> <span id="revdate">2020-08-21</span> </div> <div id="toc" class="toc"> <div id="toctitle">Table of Contents</div> <ul class="sectlevel1"> <li><a href="#_preface">Preface</a></li> <li><a href="#_samples">1. Samples</a> <ul class="sectlevel2"> <li><a href="#_binhtd_move_logs_sh">1.1. bin/htd_move_logs.sh</a></li> <li><a href="#_binp4web_base">1.2. bin/p4web_base</a></li> <li><a href="#_brokerone_per_user_sh">1.3. broker/one_per_user.sh</a></li> <li><a href="#_triggers">1.4. Triggers</a> <ul class="sectlevel3"> <li><a href="#_workflow_enforcement_triggers">1.4.1. Workflow Enforcement Triggers</a></li> <li><a href="#_checkcasetrigger_py">1.4.2. CheckCaseTrigger.py</a></li> <li><a href="#_checkchangedesc_py">1.4.3. CheckChangeDesc.py</a></li> <li><a href="#_checkfixes_py">1.4.4. CheckFixes.py</a></li> <li><a href="#_checkfixes_yaml">1.4.5. CheckFixes.yaml</a></li> <li><a href="#_checkfolderstructure_py">1.4.6. CheckFolderStructure.py</a></li> <li><a href="#_checkjobedittrigger_py">1.4.7. CheckJobEditTrigger.py</a></li> <li><a href="#_checkstreamnameformat_py">1.4.8. CheckStreamNameFormat.py</a></li> <li><a href="#_checksubmithasreview_py">1.4.9. CheckSubmitHasReview.py</a></li> <li><a href="#_controlstreamcreation_py">1.4.10. ControlStreamCreation.py</a></li> <li><a href="#_createswarmreview_py">1.4.11. CreateSwarmReview.py</a></li> <li><a href="#_defaultchangedesc_py">1.4.12. DefaultChangeDesc.py</a></li> <li><a href="#_defaultswarmreviewdesc_py">1.4.13. DefaultSwarmReviewDesc.py</a></li> <li><a href="#_defaultswarmreviewdesc_yaml">1.4.14. DefaultSwarmReviewDesc.yaml</a></li> <li><a href="#_jobincrement_pl">1.4.15. JobIncrement.pl</a></li> <li><a href="#_jobscmdfilter_py">1.4.16. JobsCmdFilter.py</a></li> <li><a href="#_p4triggers_py">1.4.17. P4Triggers.py</a></li> <li><a href="#_preventwsnonascii_py">1.4.18. PreventWsNonAscii.py</a></li> <li><a href="#_requirejob_py">1.4.19. RequireJob.py</a></li> <li><a href="#_setlabeloptions_py">1.4.20. SetLabelOptions.py</a></li> <li><a href="#_swarmreviewtemplate_py">1.4.21. SwarmReviewTemplate.py</a></li> <li><a href="#_tfsjobcheck_py">1.4.22. TFSJobCheck.py</a></li> <li><a href="#_validatecontentformat_py">1.4.23. ValidateContentFormat.py</a></li> <li><a href="#_workflow_yaml">1.4.24. Workflow.yaml</a></li> <li><a href="#_workflowtriggers_py">1.4.25. WorkflowTriggers.py</a></li> <li><a href="#_archive_long_name_trigger_pl">1.4.26. archive_long_name_trigger.pl</a></li> <li><a href="#_command_block_py">1.4.27. command_block.py</a></li> <li><a href="#_dictionary">1.4.28. dictionary</a></li> <li><a href="#_externalcopy_txt">1.4.29. externalcopy.txt</a></li> <li><a href="#_otpauthcheck_py">1.4.30. otpauthcheck.py</a></li> <li><a href="#_otpkeygen_py">1.4.31. otpkeygen.py</a></li> <li><a href="#_rad_authcheck_py">1.4.32. rad_authcheck.py</a></li> <li><a href="#_radtest_py">1.4.33. radtest.py</a></li> <li><a href="#_submit_form_1_py">1.4.34. submit_form_1.py</a></li> <li><a href="#_submit_form_1_in_py">1.4.35. submit_form_1_in.py</a></li> </ul> </li> <li><a href="#_triggers_tests">1.5. triggers / tests</a></li> </ul> </li> <li><a href="#_maintenance">2. Maintenance</a> <ul class="sectlevel2"> <li><a href="#_accessdates_py">2.1. accessdates.py</a></li> <li><a href="#_add_users_sh">2.2. add_users.sh</a></li> <li><a href="#_addusertogroup_py">2.3. addusertogroup.py</a></li> <li><a href="#_checkusers_py">2.4. checkusers.py</a></li> <li><a href="#_checkusers_not_in_group_py">2.5. checkusers_not_in_group.py</a></li> <li><a href="#_clean_protect_py">2.6. clean_protect.py</a></li> <li><a href="#_convert_label_to_autoreload_py">2.7. convert_label_to_autoreload.py</a></li> <li><a href="#_convert_rcs_to_unix_sh">2.8. convert_rcs_to_unix.sh</a></li> <li><a href="#_countrevs_py">2.9. countrevs.py</a></li> <li><a href="#_creategroups_py">2.10. creategroups.py</a></li> <li><a href="#_createusers_py">2.11. createusers.py</a></li> <li><a href="#_del_shelve_py">2.12. del_shelve.py</a></li> <li><a href="#_delusers_py">2.13. delusers.py</a></li> <li><a href="#_edge_maintenance">2.14. edge_maintenance</a></li> <li><a href="#_email_bat">2.15. email.bat</a></li> <li><a href="#_email_sh">2.16. email.sh</a></li> <li><a href="#_email_pending_client_deletes_py">2.17. email_pending_client_deletes.py</a></li> <li><a href="#_email_pending_user_deletes_py">2.18. email_pending_user_deletes.py</a></li> <li><a href="#_eviltwindetector_sh">2.19. EvilTwinDetector.sh</a></li> <li><a href="#_group_audit_py">2.20. group_audit.py</a></li> <li><a href="#_isitalabel_py">2.21. isitalabel.py</a></li> <li><a href="#_license_status_check_sh">2.22. license_status_check.sh</a></li> <li><a href="#_lowercp_py">2.23. lowercp.py</a></li> <li><a href="#_lowertree_py">2.24. lowertree.py</a></li> <li><a href="#_maintain_user_from_groups_py">2.25. maintain_user_from_groups.py</a></li> <li><a href="#_maintenance_2">2.26. maintenance</a></li> <li><a href="#_maintenance_cfg">2.27. maintenance.cfg</a></li> <li><a href="#_make_email_list_py">2.28. make_email_list.py</a></li> <li><a href="#_mirroraccess_py">2.29. mirroraccess.py</a></li> <li><a href="#_p4deleteuser_py">2.30. p4deleteuser.py</a></li> <li><a href="#_p4lock_py">2.31. p4lock.py</a></li> <li><a href="#_p4unlock_py">2.32. p4unlock.py</a></li> <li><a href="#_protect_groups_py">2.33. protect_groups.py</a></li> <li><a href="#_proxysearch_py">2.34. proxysearch.py</a></li> <li><a href="#_pymail_py">2.35. pymail.py</a></li> <li><a href="#_remove_empty_pending_changes_py">2.36. remove_empty_pending_changes.py</a></li> <li><a href="#_remove_jobs_py">2.37. remove_jobs.py</a></li> <li><a href="#_removeuserfromgroups_py">2.38. removeuserfromgroups.py</a></li> <li><a href="#_removeusersfromgroup_py">2.39. removeusersfromgroup.py</a></li> <li><a href="#_sample_cron_entries_txt">2.40. sample_cron_entries.txt</a></li> <li><a href="#_sdputils_py">2.41. sdputils.py</a></li> <li><a href="#_server_status_sh">2.42. server_status.sh</a></li> <li><a href="#_setpass_py">2.43. setpass.py</a></li> <li><a href="#_template_maintenance_cfg">2.44. template.maintenance.cfg</a></li> <li><a href="#_unload_clients_py">2.45. unload_clients.py</a></li> <li><a href="#_unload_clients_with_delete_py">2.46. unload_clients_with_delete.py</a></li> <li><a href="#_unload_labels_py">2.47. unload_labels.py</a></li> </ul> </li> </ul> </div> </div> <div id="content"> <div class="sect1"> <h2 id="_preface">Preface</h2> <div class="sectionbody"> <div class="paragraph"> <p>This page contains documentation for the SDP Unsupported folder. This folder contains sample scripts that have proven useful in the past. However, they are not maintained nor included in the automated test suite.</p> </div> <div class="paragraph"> <p>Unlike the rest of the SDP, these scripts here in this Unsupported folder are NOT officially supported. They are NOT fully tested and NOT guaranteed to work. Treat files in here with some caution, and be sure to test before using in your own environment.</p> </div> <div class="paragraph"> <p>All triggers and scripts in the <code>Unsupported</code> folder are provided as examples. They are Community supported only. Most have worked in customer environments at some point, but they are not maintained and tested at the same quality levels as the main SDP scripts.</p> </div> <div class="paragraph"> <p><strong>Please Give Us Feedback</strong></p> </div> <div class="paragraph"> <p>Perforce welcomes feedback from our users. Please send any suggestions for improving this document or the SDP to <a href="mailto:consulting@perforce.com">consulting@perforce.com</a>.</p> </div> </div> </div> <div class="sect1"> <h2 id="_samples">1. Samples</h2> <div class="sectionbody"> <div class="paragraph"> <p>Scripts and utilities in this folder are examples which were part of the SDP in the past.</p> </div> <div class="sect2"> <h3 id="_binhtd_move_logs_sh">1.1. bin/htd_move_logs.sh</h3> <div class="paragraph"> <p>Script to compress and move Helix Server structured audit logs</p> </div> <div class="paragraph"> <p>Implementation assumptions and suggestions: * Assumes the rotated log files are named audit-nnn.csv * Do NOT configure your log files to be placed in $P4ROOT * Set TARGETDIR in script</p> </div> </div> <div class="sect2"> <h3 id="_binp4web_base">1.2. bin/p4web_base</h3> <div class="paragraph"> <p>P4Web base init script for running a p4web instance. Similar function for <code>/p4/common/bin/p4d_base</code>.</p> </div> <div class="paragraph"> <p>Please refer to SDP_Guide.Unix for more details.</p> </div> <div class="paragraph"> <p>Can be used from service files or even systemd service definitions.</p> </div> <div class="paragraph"> <p>This is located here because <strong>P4Web</strong> is a deprecated and unsupported product.</p> </div> </div> <div class="sect2"> <h3 id="_brokerone_per_user_sh">1.3. broker/one_per_user.sh</h3> <div class="paragraph"> <p>This broker filter script limits commands of a given type to one process running at a time for the same user. It relies on <code>p4 monitor</code> having been enabled with <code>p4 configure set monitor=1</code> (or higher than 1).</p> </div> <div class="paragraph"> <p>This is called by the <code>p4broker</code> process. The p4broker provides an array of fields on STDIN, e.g. "command: populate" and "user: joe", which get parsed to inform the business logic of this script.</p> </div> <div class="paragraph"> <p>This is enabled by adding a block like the following to a broker config file, with this example limiting the <code>p4 populate</code> command:</p> </div> <div class="literalblock"> <div class="content"> <pre>command: ^populate$ { action = filter; execute = /p4/common/bin/broker/one_per_user.sh; }</pre> </div> </div> </div> <div class="sect2"> <h3 id="_triggers">1.4. Triggers</h3> <div class="sect3"> <h4 id="_workflow_enforcement_triggers">1.4.1. Workflow Enforcement Triggers</h4> <div class="paragraph"> <p>These triggers are documented in <a href="WorkflowEnforcementTriggers.html">HTML doc</a> or <a href="WorkflowEnforcementTriggers.pdf">PDF doc</a></p> </div> <div class="paragraph"> <p>Where appropriate below reference will be made to this section.</p> </div> </div> <div class="sect3"> <h4 id="_checkcasetrigger_py">1.4.2. CheckCaseTrigger.py</h4> <div class="paragraph"> <p>This trigger checks for attempts to add new files (including via branching or renaming) which only differ in case in <strong>some part of their path</strong> from existing files.</p> </div> <div class="paragraph"> <p>This avoids issues in these scenarios:</p> </div> <div class="ulist"> <ul> <li> <p>for a case insensitive server:</p> <div class="ulist"> <ul> <li> <p>Windows clients (case insensitive) can accidentally create new directories or files which sync into different directoroes on Unix (case sensitive)</p> </li> </ul> </div> </li> <li> <p>for a case sensitive server:</p> <div class="ulist"> <ul> <li> <p>Unix clients can create 2 (or more) files which only differ in case, and when synced to a Windows client, only one of them appears</p> </li> </ul> </div> </li> </ul> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" CaseCheckTrigger.py Example Usage (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): checkcase change-submit //... "python3 /p4/common/bin/triggers/CheckCaseTrigger.py %change% " Option where a specific user is specified to avoid trigger (e.g. git-fusion-user) CheckCaseTrigger change-submit //... "/p4/common/bin/triggers/CheckCaseTrigger.py %changelist% myuser=%user%" Old style specifying params as keywords: checkcase change-submit //... "python3 c:\perforce\scripts\CheckCaseTrigger.py %changelist% port=%serverport% user=perforce" NOTE: 'mysuser' is only used to exclude the 'git-fusion-user' from this check. Even if you are not using Git Fusion we recomend setting the trigger up this way just in case you install Git Fusion in future. Sample output: Submit validation failed -- fix problems then use 'p4 submit -c 1234'. 'CheckCaseTrigger' validation failed: Your submission has been rejected because the following files are inconsistent in their use of case with respect to existing directories Your file: '//depot/dir/test' existing file/dir: '//depot/DIR' Note: This trigger requires P4Triggers.py and the P4Python API. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_checkchangedesc_py">1.4.3. CheckChangeDesc.py</h4> <div class="paragraph"> <p>This trigger parses change descriptions to ensure that they only have the required format.</p> </div> <div class="paragraph"> <p>See source for an example of Python regexes.</p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: CheckChangeDesc.py DESCRIPTION: Check the format of a change description on submit (change-submit) Can be used together with DefaultChangeDesc.py (form-out) To install, add a line to your Perforce triggers table like the following: check-change-desc change-submit //... "python /p4/common/bin/triggers/CheckChangeDesc.py -p %serverport% -u perforce %change% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): check-change-desc change-submit //... "python /p4/common/bin/triggers/CheckChangeDesc.py %change% " You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable, and set the configuration variables below. To configure, read and modify the following lines up to the comment that reads "END OF CONFIGURATION BLOCK". You may also need to modify the definition of which fields constitute a new job based on your jobspec. This is in the allowed_job() function. """ # The error messages we give to the user ERROR_MSG = """ First line of changelist description must be: OS-NNNN and some text or WF-NNNN and some text Other lines of text allowed. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_checkfixes_py">1.4.4. CheckFixes.py</h4> <div class="paragraph"> <p>Part of <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="paragraph"> <p>This trigger is intended for use with P4DTG (Defect Tracking Replication) installations.</p> </div> <div class="paragraph"> <p>It will allows fixes to be added or deleted depending on the values of job fields. Thus you can control workflow and disallow fixes if jobs are in a particular state. So if the field JiraStatus is closed, then you are not allowed to add or delete a fix.</p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: CheckFixes.py DESCRIPTION: This trigger is intended for use with P4DTG (Defect Tracking Replication) installations. It will allows fixes to be added or deleted depending on the values of job fields. Thus you can control workflow and disallow fixes if jobs are in a particular state. So if the field JiraStatus is closed, then you are not allowed to add or delete a fix. To install, add a line to your Perforce triggers table like the following: check-fixes fix-add //... "python /p4/common/bin/triggers/CheckFixes.py -p %serverport% -u perforce %change% %client% %jobs%" check-fixes fix-delete //... "python /p4/common/bin/triggers/CheckFixes.py -p %serverport% -u perforce --delete %change% %client% %jobs%" or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): check-fixes fix-add //... "python /p4/common/bin/triggers/CheckFixes.py %change% %client% %jobs%" check-fixes fix-delete //... "python /p4/common/bin/triggers/CheckFixes.py --delete %change% %client% %jobs%" You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_checkfixes_yaml">1.4.5. CheckFixes.yaml</h4> <div class="paragraph"> <p>Sample YAML config file for <a href="#_checkfixes_py">Section 1.4.4, “CheckFixes.py”</a></p> </div> </div> <div class="sect3"> <h4 id="_checkfolderstructure_py">1.4.6. CheckFolderStructure.py</h4> <div class="paragraph"> <p>Part of <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="paragraph"> <p>This trigger will ensure that any attempt to add (or branch) a file does not create a new folder name at specific levels.</p> </div> <div class="paragraph"> <p>With new streams protections options in p4d 2020.1 this can be removed.</p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: CheckFolderStructure.py DESCRIPTION: This trigger is a change-submit trigger for workflow enforcement using Workflow.yaml for streams. It will ensure that any attempt to add (or branch) a file does not create a new folder name at specific levels. As with other Workflow triggers, it looks for a project matching files in the change. The value of "new_folder_allowed_level" is an integer and controls at which level within a stream new folder names can be created. If not set, or set to 0, then new folders can be created in the root of the stream. If set to 1, then for stream //streams/main/new_folder/a.txt would not be allowed, but //streams/main/existing_folder/new_folder/a.txt would be allowed. Note that files can be added just not folders. The field "new_folder_exceptions" - an array of users/groups who can override the trigger. To install, add a line to your Perforce triggers table like the following: check-folder-structure change-submit //... "python3 /p4/common/bin/triggers/CheckFolderStructure.py -p %serverport% -u perforce %change%" or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): check-folder-structure change-submit //... "python3 /p4/common/bin/triggers/CheckFolderStructure.py %change%" You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_checkjobedittrigger_py">1.4.7. CheckJobEditTrigger.py</h4> <div class="paragraph"> <p>For enforcment of job editing with P4DTG usage (e.g. JIRA).</p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: CheckJobEditTrigger.py DESCRIPTION: This trigger is intended for use with P4DTG (Defect Tracking Replication) installations. It can (optionally as configured): 1. Prevent creation of new jobs in Perforce by anyone other than the replication user. 2 Prevent modification of read-only fields of jobs in Perforce by anyone other than the replication user. 3. Create newly mirrored jobs using the same name as that in the defect tracker. To install, add a line to your Perforce triggers table like the following: job_save_check form-in job "python /p4/common/bin/triggers/CheckJobEditTrigger.py -p %serverport% -u perforce %user% %formfile% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): job_save_check form-in job "python /p4/common/bin/triggers/CheckJobEditTrigger.py %user% %formfile% " You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable, and set the configuration variables below. To configure, read and modify the following lines up to the comment that reads "END OF CONFIGURATION BLOCK". You may also need to modify the definition of which fields constitute a new job based on your jobspec. This is in the allowed_job() function. """ # CONFIGURATION BLOCK # The error messages we give to the user MSG_CANT_CREATE_JOBS = """ You are not allowed to create new jobs! Please create or modify the JIRA issue and let changes be replicated. """ MSG_CANT_CHANGE_FIELDS = """ You have changed one or more read-only job fields: """ # The list of writeable fields that users can change. # Changes to any other fields are rejected. # Please validate this against your jobspec. WRITEABLE_FIELDS = ["Status", "Date"] # Replicator user - this user is allowed to change fields REPLICATOR_USER = "p4dtg" JIRA_USER = "jira" # END OF CONFIGURATION BLOCK</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_checkstreamnameformat_py">1.4.8. CheckStreamNameFormat.py</h4> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: CheckStreamNameFormat.py DESCRIPTION: Trigger to only require stream names to follow a regex (on 'form-save' event) To install, add a line to your Perforce triggers table like the following: check-stream-name form-save stream "python /p4/common/bin/triggers/CheckStreamNameFormat.py -c config.yaml -p %serverport% -u perforce %formfile% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): check-stream-name form-save stream "python /p4/common/bin/triggers/CheckStreamNameFormat.py -c /p4/common/config/Workflow.yaml %formfile% " You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable, and setup the YAML configuration file (specified by -c parameter above): # ------------------------------------------ # config.yaml # msg_new_folder_not_allowed: An array of lines for the message # For legibility it is good to have the first line blank msg_invalid_stream_name: - "" - "The format of your stream name is not valid." - "Only the following formats are allowed:" # valid_stream_name_formats: An array of python regexes to apply to stream # names. Quote all values. ".*" will allow all names. valid_stream_name_formats: - "//streams/(dev|rel|main)_.*" - "//morestreams/(teamA|teamB)/(dev)_.*" """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_checksubmithasreview_py">1.4.9. CheckSubmitHasReview.py</h4> <div class="paragraph"> <p>Part of <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="paragraph"> <p>Since Swarm 2019.1 this trigger is no longer required since it can be replaced with Swarm’s Workflow functionality.</p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: CheckSubmitHasReview.py DESCRIPTION: This trigger is intended for use with Swarm installations. It will make sure that any changelist being submitted has an associated Swarm review. Note it is possible to configure a list of users who are allowed to bypass the trigger, e.g. a jenkins user who submits after building. See Workflow.yaml To install, add a line to your Perforce triggers table like the following: create_swarm_review change-submit //... "python /p4/common/bin/triggers/CheckSubmitHasReview.py -c /p4/common/config/Workflow.yaml -p %serverport% -u perforce %change% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): create_swarm_review change-submit //... "python /p4/common/bin/triggers/CheckSubmitHasReview.py -c /p4/common/config/Workflow.yaml %change% " Note that -c/--config specifies a yaml file - see Workflow.yaml for example. You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_controlstreamcreation_py">1.4.10. ControlStreamCreation.py</h4> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: ControlStreamCreation.py DESCRIPTION: Trigger to only allow certain users/groups to create streams (form-save) To install, add a line to your Perforce triggers table like the following: control-stream-create form-save stream "python /p4/common/bin/triggers/ControlStreamCreation.py -c config.yaml -p %serverport% -u perforce %user% %formfile% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): control-stream-create form-save stream "python /p4/common/bin/triggers/ControlStreamCreation.py -c /p4/common/config/Workflow.yaml %user% %formfile% " You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable, and setup the YAML configuration file (specified by -c parameter above): # ------------------------------------------ # config.yaml # msg_new_folder_not_allowed: An array of lines for the message # For legibility it is good to have the first line blank msg_cant_create_stream: - "" - "You are not allowed to create new streams." - "Check with the admins to be added to an appropriate group." # can_create_streams: An array of projects with user or group names who are allowed # to create streams matching path regexes. # name: Project name # stream_paths: An array of python regex patterns # to which the check is applied - should be quoted. Use ".*" to match everything can_create_streams: - name: Name of this project stream_paths: - "//my_streams_depot/.*" allowed_users_groups: - user1 - group1 - group2 - name: Project A stream_paths: - "//depot2/.*" allowed_users_groups: - user2 - group3 """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_createswarmreview_py">1.4.11. CreateSwarmReview.py</h4> <div class="paragraph"> <p>Part of <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="paragraph"> <p>This trigger creates Swarm reviews automatically for committed changes.</p> </div> <div class="paragraph"> <p>It is deprecated since Swarm now supports this as part of its workflow.</p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: CreateSwarmReview.py DESCRIPTION: This trigger is intended for use with Swarm installations It will search for any jobs associated with the changelist and find any reviews associated with that job. If found it will update the review with this changelist Otherwise it will create a new Swarm review with a template description and with configurable reviewers as requested. To install, add a line to your Perforce triggers table like the following: create_swarm_review change-commit //... "python /p4/common/bin/triggers/CreateSwarmReview.py -c Workflow.yaml -p %serverport% -u perforce %change% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): create_swarm_review change-commit //... "python /p4/common/bin/triggers/CreateSwarmReview.py -c Workflow.yaml %change% " Note that -c/--config specifies a yaml file - see Workflow.yaml for example. You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_defaultchangedesc_py">1.4.12. DefaultChangeDesc.py</h4> <div class="paragraph"> <p>Creates a default changelist description.</p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: DefaultChangeDesc.py DESCRIPTION: Create a default change description (form-out) Can be used together with CheckChangeDesc.py (change-submit) To install, add a line to your Perforce triggers table like the following: default-change-desc form-out change "python /p4/common/bin/triggers/DefaultChangeDesc.py -p %serverport% -u perforce %user% %formfile% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): default-change-desc form-out change "python /p4/common/bin/triggers/DefaultChangeDesc.py %user% %formfile% " You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable, and set the configuration variables below. To configure, read and modify the following lines up to the comment that reads "END OF CONFIGURATION BLOCK". You may also need to modify the definition of which fields constitute a new job based on your jobspec. This is in the allowed_job() function. """ # CONFIGURATION BLOCK # The error messages we give to the user DEFAULT_CHANGE_DESC = """OS-NNNN/WF-NNNN <Description of change> # Please choose either OS or WF JIRA issue ID and your description # Description may be multi line. # Comment lines like these beginning with '#' can be edited out or will # be removed automatically when change is submitted. # The description will be validated before you save. """ # END OF CONFIGURATION BLOCK</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_defaultswarmreviewdesc_py">1.4.13. DefaultSwarmReviewDesc.py</h4> <div class="paragraph"> <p>Updates Swarm review changelist with suitable template text. This can encourage users to record information against the review, or use a checklist.</p> </div> <div class="paragraph"> <p>Part of <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: DefaultSwarmReviewDesc.py DESCRIPTION: Create a default change description (form-in) for swarm user - so updates Swarm review changelist Can be used together with CheckChangeDesc.py (change-submit) To install, add a line to your Perforce triggers table like the following: default-swarm-desc form-in change "python /p4/common/bin/triggers/DefaultSwarmReviewDesc.py -p %serverport% -u perforce %user% %formfile% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): default-swarm-desc form-in change "python /p4/common/bin/triggers/DefaultSwarmReviewDesc.py %user% %formfile% " You may need to provide the full path to python executable, or edit the path to the trigger. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_defaultswarmreviewdesc_yaml">1.4.14. DefaultSwarmReviewDesc.yaml</h4> <div class="paragraph"> <p>Example YAML config file for <a href="#_defaultswarmreviewdesc_py">Section 1.4.13, “DefaultSwarmReviewDesc.py”</a></p> </div> </div> <div class="sect3"> <h4 id="_jobincrement_pl">1.4.15. JobIncrement.pl</h4> <div class="paragraph"> <p>Trigger to increment jobs with custom names.</p> </div> <div class="paragraph"> <p>Enable this as a form-in trigger on the job form, for example:</p> </div> <div class="literalblock"> <div class="content"> <pre>Triggers: JobIncrement form-in job "/p4/common/bin/triggers/JobIncrement.pl %formfile%"</pre> </div> </div> <div class="paragraph"> <p>The default job naming convention with Perforce Jobs has an auto-increment feature, so that if you create job with a <code>Job:</code> field value of <code>new</code>, it will be changed to jobNNNNNN, with the six-digit value being automatically incremented.</p> </div> <div class="paragraph"> <p>Perforce jobs also support custom job naming, e.g. to name jobs PROJX-123, where the name itself is more meaningful. But if you use custom job names, you forego the convenience of automatic generation of a new job number. Now typically, if the default job naming feature isn’t used, it’s because new issues originate jn an external issue tracking, so there’s no need for incrementing by Perforce; the custom job names just mirror the value in the external system.</p> </div> <div class="paragraph"> <p>This script is aims to make it easier to use custom job names with Perforce even when there is no external issue tracker integration, by providing the ability to generate new job names automatically.</p> </div> <div class="paragraph"> <p>The <code>Project:</code> field in the Jobspec has a <code>select</code> field with pre-defined values for project names. Projects desiring to use custom jobs names will define a counter named JobPrefix-<project_name>, with the value being a tag name, a short form of the project name, to be used as a prefix for job names. For example, a project named joe_schmoe-wicked-good-thing might have a prefix of WGT. Jobs will be named WGT-1, WGT-2, etc. By convention, job prefixes are comprised only of uppercase letters, matching the pattern ^[A-Z]$. No spaces, commas, dashes, underbars, etc. allowed. (There is no mechanicm for mechanical enforcement of this convention, nor none needed, as tags are defined and created by Perforce Admins).</p> </div> <div class="paragraph"> <p>To define a prefix for a project, an admin define a value for the appropriate counter, e.g. by doing:</p> </div> <div class="literalblock"> <div class="content"> <pre>p4 counter JobPrefix-some-cool-project SCT</pre> </div> </div> <div class="paragraph"> <p><strong>High Number Counter</strong></p> </div> <div class="paragraph"> <p>For projects with defined tags, there will also be a high number counter tracking the highest numbered job with a give prefix. This counter is created automatically and maintained by this script.</p> </div> <div class="paragraph"> <p>This trigger script fires as a <code>form-in</code> trigger on job forms, i.e. it fires on jobs that are on their way into the server. If <code>Job:`</code> field value is <code>new</code> and the <code>Project:</code> field value has an associated JobPrefix counter, then the name of the job is determined and set by incrementing the High Number counter, ultimately replacing the value <code>new</code> with something like SCT-201 before it ever gets to the server. If no High Number counter exists for the project, it gets set to <code>1</code>.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>JobIncrement.pl -h|-man</pre> </div> </div> <div class="paragraph"> <p>Display a usage message. The <code>-h</code> display a short synopsis only, while <code>-man</code> displays this message.</p> </div> <div class="paragraph"> <p>Return status: Zero indicates normal completion, Non-Zero indicates an error.</p> </div> </div> <div class="sect3"> <h4 id="_jobscmdfilter_py">1.4.16. JobsCmdFilter.py</h4> <div class="paragraph"> <p>This script is designed to run as a jobs command filter trigger on the server. It ensures that multiple wildcards are not specified in any search string (which can cause the server to perform excessive processing and impact performance)</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>jobs-cmd-filter command pre-user-jobs "/p4/common/bin/triggers/JobsCmdFilter.py %args%"</pre> </div> </div> </div> <div class="sect3"> <h4 id="_p4triggers_py">1.4.17. P4Triggers.py</h4> <div class="paragraph"> <p>Base class for many of the Python triggers.</p> </div> </div> <div class="sect3"> <h4 id="_preventwsnonascii_py">1.4.18. PreventWsNonAscii.py</h4> <div class="paragraph"> <p>This script is designed to run as a form save trigger on the server. It will cause a workspace save to fail if any non-ascii characters are present in the workspace spec.</p> </div> <div class="paragraph"> <p>It also will block odd characters from the workspace name.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>PreventWSNonASCII form-save client "/p4/common/bin/triggers/PreventWsNonAscii.py %formfile%"</pre> </div> </div> </div> <div class="sect3"> <h4 id="_requirejob_py">1.4.19. RequireJob.py</h4> <div class="paragraph"> <p>Allows admins to require jobs to be associated with all submits in particular areas of repository.</p> </div> <div class="paragraph"> <p>Part of <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: RequireJob.py DESCRIPTION: This is a trigger which allows admins to require a job to be linked with any submits. It takes a config file of the format for Workflow.yaml which controls whether this trigger fires or not (in addition to trigger table entries). It refers to field 'swarm_user' and allows that user, if specified, to bypass this trigger. Also one or more users specified by 'submit_without_review_users'. To install, add a line to your Perforce triggers table like the following: require-job change-submit //... "python /p4/common/bin/triggers/RequireJob.py -c /p4/common/config/Workflow.yaml -p %serverport% -u perforce %change% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): require-job change-submit //... "python /p4/common/bin/triggers/RequireJob.py -c /p4/common/config/Workflow.yaml %change% " You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_setlabeloptions_py">1.4.20. SetLabelOptions.py</h4> <div class="paragraph"> <p>This script is designed to run as a form-in and form-out trigger on the server. It sets the autoreload option for static labels and doesn’t allow the user to change it.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>setlabelopts form-out label "/p4/common/bin/triggers/SetLabelOptions.py %formfile%" setlabelopts form-in label "/p4/common/bin/triggers/SetLabelOptions.py %formfile%"</pre> </div> </div> </div> <div class="sect3"> <h4 id="_swarmreviewtemplate_py">1.4.21. SwarmReviewTemplate.py</h4> <div class="paragraph"> <p>Part of <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: SwarmReviewTemplate.py DESCRIPTION: This trigger is intended for use with Swarm installations and other workflow triggers. It is intended to fire as a shelve-commit trigger which only fires for the swarm user. It updates the Review description with template text as appropriate. It will search for any jobs associated with the changelist and find any reviews associated with that job. If found it will update the review with this changelist To install, add a line to your Perforce triggers table like the following: swarm_template_review shelve-commit //... "python /p4/common/bin/triggers/SwarmReviewTemplate.py -c Workflow.yaml -p %serverport% -u perforce %change% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): swarm_template_review shelve-commit //... "python /p4/common/bin/triggers/SwarmReviewTemplate.py -c Workflow.yaml %change% " Note that -c/--config specifies a yaml file - see Workflow.yaml for example. You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_tfsjobcheck_py">1.4.22. TFSJobCheck.py</h4> <div class="paragraph"> <p>Simple trigger to ensure jobs always contain a particular string.</p> </div> <div class="paragraph"> <p>Note that <a href="#_checkchangedesc_py">Section 1.4.3, “CheckChangeDesc.py”</a> is a more general form of this trigger.</p> </div> <div class="paragraph"> <p>Trigger table entry:</p> </div> <div class="literalblock"> <div class="content"> <pre>TFSJobCheck change-submit //depot/path/... "/p4/common/bin/triggers/TFSJobCheck.py %changelist%"</pre> </div> </div> </div> <div class="sect3"> <h4 id="_validatecontentformat_py">1.4.23. ValidateContentFormat.py</h4> <div class="paragraph"> <p>This trigger is intended for file content validation as part of change-content trigger Works for YAML, XML - only checks files matching required file extensions - see below.</p> </div> <div class="paragraph"> <p>Particularly useful to ensure that <code>Workflow.yaml</code> file itself doesn’t get accidentally corrupted!</p> </div> <div class="paragraph"> <p>Part of <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code>""" NAME: ValidateContentFormat.py DESCRIPTION: This trigger is intended for file content validation as part of change-content trigger Works for YAML, XML - only checks files matching required file extensions - see below. To install, add a line to your Perforce triggers table like the following: validate-yaml change-content //....yaml "python /p4/common/bin/triggers/ValidateContentFormat.py -p %serverport% -u perforce --yaml %change% " validate-xml change-content //....xml "python /p4/common/bin/triggers/ValidateContentFormat.py -p %serverport% -u perforce --xml %change% " or (if server is standard SDP and has appropriate environment defaults for P4PORT and P4USER): validate-yaml change-content //....yaml "python /p4/common/bin/triggers/ValidateContentFormat.py --yaml %change% " validate-xml change-content //....xml "python /p4/common/bin/triggers/ValidateContentFormat.py --xml %change% " You may need to provide the full path to python executable, or edit the path to the trigger. Also, don't forget to make the file executable. """</code></pre> </div> </div> </div> <div class="sect3"> <h4 id="_workflow_yaml">1.4.24. Workflow.yaml</h4> <div class="paragraph"> <p>Sample generic configuration file for use with <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> </div> <div class="sect3"> <h4 id="_workflowtriggers_py">1.4.25. WorkflowTriggers.py</h4> <div class="paragraph"> <p>Base class for use by various Workflow triggers as per <a href="#_workflow_enforcement_triggers">Section 1.4.1, “Workflow Enforcement Triggers”</a></p> </div> <div class="paragraph"> <p>Imports <a href="#_p4triggers_py">Section 1.4.17, “P4Triggers.py”</a></p> </div> </div> <div class="sect3"> <h4 id="_archive_long_name_trigger_pl">1.4.26. archive_long_name_trigger.pl</h4> <div class="paragraph"> <p>This script unconverts #@*% from ascii in archive files.</p> </div> <div class="paragraph"> <p>The reason for doing this is when they are expanded they can overflow the unix file path length.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>$file_prefix -op <operation> -rev <revision> -lbr <path/file> < stdin</pre> </div> </div> <div class="paragraph"> <p>Trigger:</p> </div> <div class="literalblock"> <div class="content"> <pre>arch archive //... "/p4/common/bin/triggers/archive_long_name_trigger.pl -op %op% -lbr %file% -rev %rev%"</pre> </div> </div> </div> <div class="sect3"> <h4 id="_command_block_py">1.4.27. command_block.py</h4> <div class="paragraph"> <p>This is command trigger to allow you to block commands from all but listed users.</p> </div> <div class="paragraph"> <p>Trigger table entry examples:</p> </div> <div class="literalblock"> <div class="content"> <pre>command-block command pre-user-obliterate "/p4/common/bin/triggers/command_block.py %user%" command-block command pre-user-(obliterate|protect$) "/p4/common/bin/triggers/command_block.py %user%"</pre> </div> </div> </div> <div class="sect3"> <h4 id="_dictionary">1.4.28. dictionary</h4> <div class="paragraph"> <p>This file contains dictionary translations for parsing requests and generating responses.</p> </div> <div class="paragraph"> <p>Used by <a href="#_rad_authcheck_py">Section 1.4.32, “rad_authcheck.py”</a></p> </div> </div> <div class="sect3"> <h4 id="_externalcopy_txt">1.4.29. externalcopy.txt</h4> <div class="paragraph"> <p>Documents how to use externalcopy programs to transfer data between commit and edge.</p> </div> </div> <div class="sect3"> <h4 id="_otpauthcheck_py">1.4.30. otpauthcheck.py</h4> <div class="paragraph"> <p>This trigger will check to see if the userid is in a the LOCL_PASSWD_FILE first and authenticate with that if found. If the users isn’t in the local file, it checks to see if the user is a service user, and if so, it will authenticate against just the auth server. Finally, it will check the user’s password and authenticator token if the other two conditions don’t match.</p> </div> <div class="paragraph"> <p>The trigger table entry is:</p> </div> <div class="literalblock"> <div class="content"> <pre>authtrigger auth-check auth "/p4/common/bin/triggers/vip_authcheckTrigger.py %user% %serverport%"</pre> </div> </div> </div> <div class="sect3"> <h4 id="_otpkeygen_py">1.4.31. otpkeygen.py</h4> <div class="paragraph"> <p>This script generates a key for a user and stores the key in the Perforce server.</p> </div> <div class="paragraph"> <p>Used together with <a href="#_otpauthcheck_py">Section 1.4.30, “otpauthcheck.py”</a></p> </div> </div> <div class="sect3"> <h4 id="_rad_authcheck_py">1.4.32. rad_authcheck.py</h4> <div class="paragraph"> <p>This trigger will check to see if the userid is in a the LOCL_PASSWD_FILE first and authenticate with that if found. If the users isn’t in the local file, it checks to see if the user is a service user, and if so, it will authenticate against LDAP. Finally, it will check the user against the Radius server if the other two conditions don’t match.</p> </div> <div class="paragraph"> <p>You need to install the python-pyrad package and python-six package for it to work. It also needs the file named dictionary in the same directory as the script.</p> </div> <div class="paragraph"> <p>Set the Radius servers in RAD_SERVERS below Set the shared secret Pass in the user name as a parameter and the password from stdin.</p> </div> <div class="paragraph"> <p>The trigger table entry is:</p> </div> <div class="literalblock"> <div class="content"> <pre>authtrigger auth-check auth "/p4/common/bin/triggers/rad_authcheck.py %user% %serverport% %clientip%"</pre> </div> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <i class="fa icon-note" title="Note"></i> </td> <td class="content"> The script current is set such that the Perforce user names should match the RSA ID’s. In the case of one customer, the RSA ID’s were all numeric, so we made the Perforce usernames be realname_RSAID and had this script just strip off the realname_ part. Example commented out in the main function. </td> </tr> </table> </div> </div> <div class="sect3"> <h4 id="_radtest_py">1.4.33. radtest.py</h4> <div class="paragraph"> <p>This is a radius test script. You need to install the python-pyrad package and python-six package for it to work. It also needs the file named dictionary in the same directory as the script.</p> </div> <div class="paragraph"> <p>Set the Radius servers in radsvrs below Set the shared secret Pass in the user name as a parameter and the password from stdin.</p> </div> </div> <div class="sect3"> <h4 id="_submit_form_1_py">1.4.34. submit_form_1.py</h4> <div class="paragraph"> <p>This script modifies the description of the change form for the Perforce users listed in the submit_form_1_users group.</p> </div> <div class="paragraph"> <p>Trigger table entry:</p> </div> <div class="literalblock"> <div class="content"> <pre>submitform1 form-out change "/p4/common/bin/triggers/submit_form_1.py %formfile% %user%"</pre> </div> </div> </div> <div class="sect3"> <h4 id="_submit_form_1_in_py">1.4.35. submit_form_1_in.py</h4> <div class="paragraph"> <p>This script checks the input of the description form for the path specified in the triggers table.</p> </div> <div class="paragraph"> <p>Trigger table entry:</p> </div> <div class="literalblock"> <div class="content"> <pre>submitform1_in change-submit //depot/somepath/... "/p4/common/bin/triggers/submit_form_1_in.py %changelist% %user%"</pre> </div> </div> </div> </div> <div class="sect2"> <h3 id="_triggers_tests">1.5. triggers / tests</h3> <div class="paragraph"> <p>This directory contains test harnesses for various triggers document in the section above.</p> </div> <div class="paragraph"> <p>They import a common base class <code>p4testutils.py</code> and then run various unit tests.</p> </div> <div class="paragraph"> <p>The tests are one of two types: * simple unit tests * integration tests using a real <code>p4d</code> instance (running in DVCS-style mode).</p> </div> <div class="paragraph"> <p>The tests make it straight forward to ensure that you haven’t broken any tests.</p> </div> <div class="paragraph"> <p>You need to ensure you have a <code>p4d</code> executable in your PATH.</p> </div> <div class="paragraph"> <p>Run any individual test:</p> </div> <div class="literalblock"> <div class="content"> <pre>python3 TestRequireJob.py</pre> </div> </div> </div> </div> </div> <div class="sect1"> <h2 id="_maintenance">2. Maintenance</h2> <div class="sectionbody"> <div class="paragraph"> <p>These are example scripts which have proven useful in the past.</p> </div> <div class="paragraph"> <p>They are NOT fully tested and NOT guaranteed to work, although in most cases they are fairly simple and reliable.</p> </div> <div class="paragraph"> <p>Treat with some caution!!!</p> </div> <div class="sect2"> <h3 id="_accessdates_py">2.1. accessdates.py</h3> <div class="paragraph"> <p>This script is normally called by another script, such as <a href="#_unload_clients_py">Section 2.45, “unload_clients.py”</a></p> </div> <div class="paragraph"> <p>However, if run standalone, it will generate 4 files with a list of specs that should be archived based on the number of weeks in <a href="#_maintenance_cfg">Section 2.27, “maintenance.cfg”</a></p> </div> <div class="paragraph"> <p>The file generated are:</p> </div> <div class="literalblock"> <div class="content"> <pre>branches.txt clients.txt labels.txt users.txt</pre> </div> </div> </div> <div class="sect2"> <h3 id="_add_users_sh">2.2. add_users.sh</h3> <div class="paragraph"> <p>This script adds a bunch of users from a users_to_add.csv file of the form:</p> </div> <div class="literalblock"> <div class="content"> <pre><user>,<email>,<full_name>[,<group1 group2 group3 ...]</pre> </div> </div> <div class="paragraph"> <p>This first line of the <code>users_to_add.csv</code> file is assumed to be a header and is always ignored.</p> </div> <div class="literalblock"> <div class="content"> <pre>vi users_to_add.csv</pre> </div> </div> <div class="literalblock"> <div class="content"> <pre>./add_users.sh 2>&1 | tee add_users.$(date +'%Y%m%d-%H%M').log</pre> </div> </div> </div> <div class="sect2"> <h3 id="_addusertogroup_py">2.3. addusertogroup.py</h3> <div class="paragraph"> <p>This script adds a user or users to the specified group.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>python addusertogroup.py [instance] user group</pre> </div> </div> <div class="ulist"> <ul> <li> <p>instance defaults to 1 if not given.</p> </li> <li> <p>user = user_name or a file containing a list of user names, one per line.</p> </li> <li> <p>group = name of Perforce group to add the user(s) to.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_checkusers_py">2.4. checkusers.py</h3> <div class="paragraph"> <p>This script will generate a list of all user names that are listed in any group, but do not have a user account on the server. The results are written to <code>removeusersfromgroups.txt</code>.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>python checkusers.py</pre> </div> </div> <div class="paragraph"> <p>You can pass that to <code>removeuserfromgroups.py</code> to do the cleanup.</p> </div> </div> <div class="sect2"> <h3 id="_checkusers_not_in_group_py">2.5. checkusers_not_in_group.py</h3> <div class="paragraph"> <p>This script will generate a list of all standard Perforce user names that are not listed in any group. It prints the results to the screen, so you may want to redirect it to a file.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>python checkusers_not_in_group.py</pre> </div> </div> </div> <div class="sect2"> <h3 id="_clean_protect_py">2.6. clean_protect.py</h3> <div class="paragraph"> <p>This script will drop all lines in the protect tabel that have a group referenced from the file <code>groups.txt</code> passed into the script. The list of groups to drop is passed in as the first parameter and the protect table is passed in as the 2nd parameter.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>python protect_groups.py remove_groups.txt p4.protect</pre> </div> </div> <div class="paragraph"> <p><code>remove_groups.txt</code> is generated using <a href="#_protect_groups_py">Section 2.33, “protect_groups.py”</a> - See that script for details.</p> </div> <div class="paragraph"> <p>Run <code>p4 protect -o > p4.protect</code> to generate the protections table.</p> </div> <div class="paragraph"> <p>You can redirect the output of this script to a file called <code>new.p4.protect</code> and then you can compare the original <code>p4.protect</code> and the <code>new.p4.protect</code>. If everything looks okay, you can update the protections table by running:</p> </div> <div class="literalblock"> <div class="content"> <pre>p4 protect -i < new.p4.protect</pre> </div> </div> </div> <div class="sect2"> <h3 id="_convert_label_to_autoreload_py">2.7. convert_label_to_autoreload.py</h3> <div class="paragraph"> <p>Converts label and sets <code>autoreload</code> option <a href="https://www.perforce.com/manuals/cmdref/Content/CmdRef/p4_label.html#Form_Fields_..503">see Command Reference</a></p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>convert_label_to_autoreload.py <label or file_with_list_of_labels></pre> </div> </div> </div> <div class="sect2"> <h3 id="_convert_rcs_to_unix_sh">2.8. convert_rcs_to_unix.sh</h3> <div class="paragraph"> <p>Executes command which converts Windows RCS files with CRLF endings to Unix LF endings:</p> </div> <div class="literalblock"> <div class="content"> <pre>find . -type f -name '*,v' -print -exec perl -p -i -e 's/\r\n/\n/' {} \;</pre> </div> </div> </div> <div class="sect2"> <h3 id="_countrevs_py">2.9. countrevs.py</h3> <div class="paragraph"> <p>This script will count the total number of files and revisions from a list of files in the Perforce server.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>p4 files //... > files.txt</pre> </div> </div> <div class="paragraph"> <p>then run:</p> </div> <div class="literalblock"> <div class="content"> <pre>python countrevs.py files.txt</pre> </div> </div> </div> <div class="sect2"> <h3 id="_creategroups_py">2.10. creategroups.py</h3> <div class="paragraph"> <p>This script creates groups on the server based on the entries in a local file called <code>groups.txt</code> which contains lines in this format:</p> </div> <div class="literalblock"> <div class="content"> <pre>group,groupname1 username1 username2 : group,groupname2 username2 username3</pre> </div> </div> <div class="paragraph"> <p>Run:</p> </div> <div class="literalblock"> <div class="content"> <pre>python creategroups [instance]</pre> </div> </div> <div class="paragraph"> <p>Instance defaults to 1 if not given.</p> </div> </div> <div class="sect2"> <h3 id="_createusers_py">2.11. createusers.py</h3> <div class="paragraph"> <p>This script will create a group of users all at once based on an input file. The input file should contain the users in the following format, one per line:</p> </div> <div class="literalblock"> <div class="content"> <pre>user,email,fullname</pre> </div> </div> <div class="paragraph"> <p>Run</p> </div> <div class="literalblock"> <div class="content"> <pre>python createusers.py userlist.csv <instance></pre> </div> </div> <div class="paragraph"> <p>Instance defaults to 1 if not passed in.</p> </div> </div> <div class="sect2"> <h3 id="_del_shelve_py">2.12. del_shelve.py</h3> <div class="paragraph"> <p>This script will delete shelves and clients that have not been accessed in the number of weeks defined by the variable weeks in <code>maintenance.cfg</code>.</p> </div> <div class="paragraph"> <p>Run the script as:</p> </div> <div class="literalblock"> <div class="content"> <pre>del_shelve.py [instance]</pre> </div> </div> <div class="paragraph"> <p>If no instance is given, it defaults to 1.</p> </div> </div> <div class="sect2"> <h3 id="_delusers_py">2.13. delusers.py</h3> <div class="paragraph"> <p>Calls <a href="#_p4deleteusers_py">[_p4deleteusers_py]</a> automain module which will remove allusers that haven’t accessed Perforce in the variable userweeks set in <code>maintenance.cfg</code></p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>delusers.py</pre> </div> </div> </div> <div class="sect2"> <h3 id="_edge_maintenance">2.14. edge_maintenance</h3> <div class="paragraph"> <p>Runs regular tasks such as:</p> </div> <div class="ulist"> <ul> <li> <p>removes server.locks dir</p> </li> <li> <p>unloading clients</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_email_bat">2.15. email.bat</h3> <div class="paragraph"> <p>This script is used to send email to all of your Perforce users.</p> </div> <div class="paragraph"> <p>Create a file called <code>message.txt</code> that contains the body of your message. Then call this script and pass the subject in quotes as the only parameter.</p> </div> <div class="paragraph"> <p>It makes a copy of the previous email list, then call make_email_list.py to generate a new one from Perforce.</p> </div> <div class="paragraph"> <p>The reason for making the copy is so that you will always have an email list that you can use to send email with. Just comment out the call to <code>python make_email_list.py</code> in the script and run it. It will use the current list to send email from. This is handy in case your server goes off-line.</p> </div> </div> <div class="sect2"> <h3 id="_email_sh">2.16. email.sh</h3> <div class="paragraph"> <p>Unix version of <a href="#_email_bat">Section 2.15, “email.bat”</a></p> </div> </div> <div class="sect2"> <h3 id="_email_pending_client_deletes_py">2.17. email_pending_client_deletes.py</h3> <div class="paragraph"> <p>This script will email users that their clients haven’t been accessed in the number of weeks defined in the weeks variable (of <code>maintenance.cfg</code>), and warn them that it will be deleted in one week if they do not use it.</p> </div> </div> <div class="sect2"> <h3 id="_email_pending_user_deletes_py">2.18. email_pending_user_deletes.py</h3> <div class="paragraph"> <p>This script will email users that their user accounts haven’t been accessed in the number of weeks defined in the weeks variable (of <code>maintenance.cfg</code>), and warn them that it will be deleted in one week if they do not use it.</p> </div> </div> <div class="sect2"> <h3 id="_eviltwindetector_sh">2.19. EvilTwinDetector.sh</h3> <div class="paragraph"> <p>Detects "evil twins".</p> </div> <div class="listingblock"> <div class="title">Usage</div> <div class="content"> <pre class="highlight"><code> echo "USAGE for $THISSCRIPT v$Version: $THISSCRIPT [-i <instance>] { -d <stream_depot> | -s //source/stream } [-w <work_dir>] [-f] [-L <log>] [-si] [-v<n>] [-D] or $THISSCRIPT [-h|-man|-V] " if [[ $style == -man ]]; then echo -e " DESCRIPTION: Detect and optionally fix \"evil twins\" in a given stream depot. The term \"evil twin\" has origins in the ClearCase version control system, the first to have truly sophistcated branching and merging capability. With this sophistication came compelixty, and an evil twin is one such complexity. An evil twin can occur in any version control system with sophisticated branching and merging (Perforce, Git, ClearCase, AccuRev, etc.). In Perforce, an evil twin occurs when two files with the same name relative to the root of a stream are created in both streams with 'p4 add', rather than the preferred workflow of creating src/foo.c in one stream and branching it into another. When a file is created with 'p4 add' twice rather than the preferred 'add then branch' flow, there is no integration history connecting the files. This creates a problem when merging across streams, as their is no 'base' or common ancestor that is needed to calculate merge results. In ClearCase, this was a bad sitution indeed - one such file needed to be designated as the \"evil\" twin, and that file and its history would need to be obliterated ('rmelem' in ClearCase parlance). In Perforce, the situation is not nearly so bad. Rather than designating one of the twins as evil, we simply do a 'baseless integration' and establish a family history, after which point the two twins can have a happy family reunion. Note that many ClearClase sites deployed an Evil Twin prevention trigger that could prevent evil twins from occuring in the first place, and deploying such a trigger is a best practice in ClearCase environments. There is a similar trigger for Perforce, but it is not commonly deployed, since evil twins aren't nearly as problematic in Perforce. They don't occur often. At a high level: * Evil twin detection means doing an integrate preview, and checking for the \"can't integrate without -i flag\" warning. The -i flag is for baseless integrations, essentially evil twins. * Evil twin correction means doing an integrate with the -i flag on the individual files, and then doing a 'p4 resolve -ay' (accept \"yours,\" i.e. accept target), and submitting. That creates an integration record, visible as a merge arrow in P4V. This solution using 'p4 resolve -ay' is the best choice for mass detection and resolution of evil twins. The assumption here is that the contents are already correct, and you only want to draw merge arrows and avoid changing content in the processing. For evil twins that occur organically in natural development, e.g. due to developer error or miscoordination among developers working in different streams, you might choose to resolve with -ay or -at (accept \"theirs,\" i.e. accept source). Natural evil twins are uncommon and occur typically only for individual files. (And if they are common, that could be a sign of a larger process/communication issue among teams). === WARNING WARNING WARING === This script may require a large amount of temporary storage, as it creates a large number of stream workspaces and does a sync in them. See the '-w' flag. OPTIONS: -i <instance> Specify the SDP instance name. This is required unless the SDP environment has previously been loaded in the current shell, i.e. by sourcing the $P4CBIN/p4_vars file and specifying the instance, e.g. : source $P4CBIN 1 -d <stream_sdepot> Specify a stream depot to process, e.g. -d fgs to process the //fgs stream depot. This argument is required unless '-s //source/stream' is specified, in which case the stream depot is inferred from the stream path. -s //source/stream Specify a particular source stream to search. The specified stream and its parent will be checked for evil twins. -w <work_dir> Specify a working directory, used for temporary workspace storage, etc. The default is \"/scratch\". === WARNING WARNING WARING === This script may require a large amount of temporary storage, as it creates a large number of stream workspaces and does a sync in them. This flag controls the scratch storage area used by this script. The workspace storage directory will be in an \"etd\" directory below the specified <work_dir> So by default, workspace root directories will appear one directory level below \"/scratch/etd\". This script will attempt to create the \"etd\" directory under specified <work_dir> if it does not already exist, and abort if it cannot be created. -f Fix evil twins with forced integrates doing baseless merges and resolving with '-ay', effectively drawing merge arrows to establish a branching relationshiop, but not affecting target file content. By default, a report shows the commands that would be run, but they are not executed. -v<n> Set verbosity 1-5 (-v1 = quiet, -v5 = highest). -L <log> Specify the path to a log file, or the special value 'off' to disable logging. All output (stdout and stderr) are captured in the log. NOTE: This script is self-logging. That is, output displayed on the screen is simultaneously captured in the log file. Do not run this script with redirection operators like '> log' or '2>&1', and do not use 'tee.' -si Operate silently. All output (stdout and stderr) is redirected to the log only; no output appears on the terminal. This cannot be used with '-L off'. -D Set extreme debugging verbosity. HELP OPTIONS: -h Display short help message -man Display man-style help message -V Dispay version info for this script and its libraries. EXAMPLES: First, setup environment: cd $P4CBIN source ./p4_vars 1 Then run as per the following examples, suitable for a long-running script. Example 1: Search for all Evil Twins in the fgs depot: nohup ./$THISSCRIPT -i 1 -w /big/scratch -d fgs < /dev/null > /tmp/etd.log 2>&1 & tailf \$(ls -t \$LOGS/${THISSCRIPT%.sh}.*.log|head -1) Example 2: Find and fix all Evil Twins in the fgs depot: nohup ./$THISSCRIPT -i 1 -w /big/scratch -d fgs -f < /dev/null > /tmp/etd.log 2>&1 & tailf \$(ls -t \$LOGS/${THISSCRIPT%.sh}.*.log|head -1) Example 3: Find and fix all Evil twins between //fgs/dev and its parent. nohup ./$THISSCRIPT -i 1 -w /big/scratch -s //fgs/dev < /dev/null > /tmp/etd.log 2>&1 & tailf \$(ls -t \$LOGS/${THISSCRIPT%.sh}.*.log|head -1) "</code></pre> </div> </div> </div> <div class="sect2"> <h3 id="_group_audit_py">2.20. group_audit.py</h3> <div class="paragraph"> <p>This script emails the owners of each group with instructions on how to validate the membership of the groups they own.</p> </div> </div> <div class="sect2"> <h3 id="_isitalabel_py">2.21. isitalabel.py</h3> <div class="paragraph"> <p>Determine if a label exists in Perforce.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>isitalabel.py labelname</pre> </div> </div> <div class="paragraph"> <p>Program will print out results of the search. Sets ISALABEL in the environment to 0 if found, 1 if not found. Also will exit with errorlevel 1 if the label is not found</p> </div> </div> <div class="sect2"> <h3 id="_license_status_check_sh">2.22. license_status_check.sh</h3> <div class="paragraph"> <p>Determines how many days/hours etc are remaining for a license.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>license_status_check.sh <instance></pre> </div> </div> </div> <div class="sect2"> <h3 id="_lowercp_py">2.23. lowercp.py</h3> <div class="paragraph"> <p>This script will make a lowercase copy of the source folder, and report any conflicts found during the copy. Run this script from the source path adjust the target path below.</p> </div> <div class="paragraph"> <p>Pass in the folder to be copied in lower case to the target.</p> </div> <div class="paragraph"> <p>ie: To copy <code>/p4/1/depots/depot</code> to <code>/depotdata2/p4/1/depots</code>, run:</p> </div> <div class="literalblock"> <div class="content"> <pre>cd /p4/1/depots lowercp.py depot</pre> </div> </div> </div> <div class="sect2"> <h3 id="_lowertree_py">2.24. lowertree.py</h3> <div class="paragraph"> <p>This script is used to rename a tree to all lowercase. It is helpful if you are trying to convert your server from case sensitive to case insensitive.</p> </div> <div class="paragraph"> <p>It takes the directory to convert as the first parameter.</p> </div> </div> <div class="sect2"> <h3 id="_maintain_user_from_groups_py">2.25. maintain_user_from_groups.py</h3> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>maintain_user_froup_groups.py [instance]</pre> </div> </div> <div class="paragraph"> <p>Defaults to instance 1 if parameter not given.</p> </div> <div class="paragraph"> <p>What this scripts does:</p> </div> <div class="literalblock"> <div class="content"> <pre>Reads users from groups Creates any missing user accounts Removes accounts that are not in the group</pre> </div> </div> </div> <div class="sect2"> <h3 id="_maintenance_2">2.26. maintenance</h3> <div class="paragraph"> <p>This is an example maintenance script to run the recommended maintenance scripts on a weekly basis. You need to make sure you update the hard coded locations to match yours.</p> </div> </div> <div class="sect2"> <h3 id="_maintenance_cfg">2.27. maintenance.cfg</h3> <div class="paragraph"> <p>Created from <a href="#_template_maintenance_cfg">Section 2.44, “template.maintenance.cfg”</a></p> </div> </div> <div class="sect2"> <h3 id="_make_email_list_py">2.28. make_email_list.py</h3> <div class="paragraph"> <p>This script creates a list of email address for your users directly from their Perforce user accounts. It is intended to be used as part of email.bat, but can be used with any mail program that can read addresses from a list.</p> </div> <div class="paragraph"> <p>This can be replaced by a single command using recent options for p4 CLI:</p> </div> <div class="literalblock"> <div class="content"> <pre>p4 -F "%email%|%user%" users</pre> </div> </div> </div> <div class="sect2"> <h3 id="_mirroraccess_py">2.29. mirroraccess.py</h3> <div class="paragraph"> <p>This script will add a user to all the groups of another user in Perforce.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>python mirroraccess.py instance user1 user2 <user3> <user4> ... <userN></pre> </div> </div> <div class="ulist"> <ul> <li> <p>user1 = user to mirror access from.</p> </li> <li> <p>user2 = user to mirror access to.</p> </li> <li> <p><user3> …​ <userN> = additional users to mirror access to.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_p4deleteuser_py">2.30. p4deleteuser.py</h3> <div class="paragraph"> <p>Removes user and any clients/shelves owned by that user.</p> </div> <div class="literalblock"> <div class="content"> <pre>Note: This script will pull all the servers addresses off the servers output. In order for that to work, you must add the Address field on each server form.</pre> </div> </div> <div class="literalblock"> <div class="content"> <pre>Usage:</pre> </div> </div> <div class="literalblock"> <div class="content"> <pre>p4deleteuser.py [instance] user_to_remove p4deleteuser.py [instance] file_with_users_to_remove</pre> </div> </div> </div> <div class="sect2"> <h3 id="_p4lock_py">2.31. p4lock.py</h3> <div class="paragraph"> <p>This script locks a perforce label.</p> </div> <div class="paragraph"> <p>The only command line parameter is the label</p> </div> <div class="literalblock"> <div class="content"> <pre>p4lock.py LABEL</pre> </div> </div> <div class="paragraph"> <p>See <a href="#_p4unlock_py">Section 2.32, “p4unlock.py”</a></p> </div> </div> <div class="sect2"> <h3 id="_p4unlock_py">2.32. p4unlock.py</h3> <div class="paragraph"> <p>This script unlocks a perforce label.</p> </div> <div class="paragraph"> <p>The only command line parameter is the label</p> </div> <div class="literalblock"> <div class="content"> <pre>p4unlock.py labelname</pre> </div> </div> </div> <div class="sect2"> <h3 id="_protect_groups_py">2.33. protect_groups.py</h3> <div class="paragraph"> <p>This script will list all the groups mentioned in "p4 protect" that are not a Perforce group.</p> </div> <div class="paragraph"> <p>You need to pull the groups from p4 protect with:</p> </div> <div class="literalblock"> <div class="content"> <pre>p4 protect -o | grep group | cut -d " " -f 3 | sort | uniq > protect_groups.txt and pass protect_groups.txt to this script.</pre> </div> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>python protect_groups.py [instance] protect_groups.txt > remove_groups.txt</pre> </div> </div> <div class="ulist"> <ul> <li> <p>instance defaults to 1 if not given.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_proxysearch_py">2.34. proxysearch.py</h3> <div class="paragraph"> <p>This script will search your server log file and find any proxy servers that are connecting to the server. The server log file needs to be set to a debug level 1 or higher in order to record all the commands coming into the server.</p> </div> <div class="paragraph"> <p>Just pass the log file in as parameter to this script and it will print out a list of the proxy servers.</p> </div> </div> <div class="sect2"> <h3 id="_pymail_py">2.35. pymail.py</h3> <div class="paragraph"> <p>Reads values from <code>maintenance.cfg</code> for things like <code>mailhost</code>. Sends email according to parameters below.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>pymail.py -t <to-address or address file> -s <subject> -i <input file></pre> </div> </div> </div> <div class="sect2"> <h3 id="_remove_empty_pending_changes_py">2.36. remove_empty_pending_changes.py</h3> <div class="paragraph"> <p>This script will remove all of the empty pending changelists on the server.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>remove_empty_pending_changes.py</pre> </div> </div> </div> <div class="sect2"> <h3 id="_remove_jobs_py">2.37. remove_jobs.py</h3> <div class="paragraph"> <p>This script will remove all of the fixes associated with jobs and then delete the jobs listed in the file passed as the first argument. The list can be created with <code>p4 jobs > jobs.txt</code>. The script will handles the extra text in the lines.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>remove_jobs.py [SDP_INSTANCE] list_of_jobs_to_remove</pre> </div> </div> </div> <div class="sect2"> <h3 id="_removeuserfromgroups_py">2.38. removeuserfromgroups.py</h3> <div class="paragraph"> <p>This script will look for the specified users in all groups and remove them.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>removeuserfromgroups.py [instance] USER removeuserfromgroups.py [instance] FILE</pre> </div> </div> <div class="ulist"> <ul> <li> <p>USER can be a single username or, it can be a FILE with a list of users.</p> </li> <li> <p>instance defaults to 1 if not given.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_removeusersfromgroup_py">2.39. removeusersfromgroup.py</h3> <div class="paragraph"> <p>This script will remove the specified users from given group.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>removeusersfromgroup.py [instance] USER groupname removeusersfromgroup.py [instance] FILE groupname</pre> </div> </div> <div class="ulist"> <ul> <li> <p>USER can be a single username or, it can be a FILE with a list of users.</p> </li> <li> <p>instance defaults to 1 if not given.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_sample_cron_entries_txt">2.40. sample_cron_entries.txt</h3> <div class="paragraph"> <p>These is a sample crontab entry to call the <code>maintenance</code> script from cron.</p> </div> </div> <div class="sect2"> <h3 id="_sdputils_py">2.41. sdputils.py</h3> <div class="paragraph"> <p>This module is a lbrary for other modules in this directory and is imported by some of them.</p> </div> </div> <div class="sect2"> <h3 id="_server_status_sh">2.42. server_status.sh</h3> <div class="paragraph"> <p>This script is used to check the status of all the instances running on this machine. It can handle named and numbered instances.</p> </div> </div> <div class="sect2"> <h3 id="_setpass_py">2.43. setpass.py</h3> <div class="paragraph"> <p>This script will set the password for a user to the value set in the password variable in the main function.</p> </div> <div class="paragraph"> <p>The name of the user to set the password for is passed as a parameter to the file.</p> </div> <div class="paragraph"> <p>Usage:</p> </div> <div class="literalblock"> <div class="content"> <pre>python setpass.py [instance] user</pre> </div> </div> <div class="ulist"> <ul> <li> <p>instance defaults to 1 if not given.</p> </li> </ul> </div> </div> <div class="sect2"> <h3 id="_template_maintenance_cfg">2.44. template.maintenance.cfg</h3> <div class="paragraph"> <p>Example for editing and copying to <code>maintenance.cfg</code>.</p> </div> <div class="paragraph"> <p>Read by many of the scripts.</p> </div> <div class="paragraph"> <p>Values include things like default number of weeks to use to deted <strong>old</strong> users/clients/labels.</p> </div> </div> <div class="sect2"> <h3 id="_unload_clients_py">2.45. unload_clients.py</h3> <div class="paragraph"> <p>This script will unload clients not accessed since the weeks parameter specified in <code>maintenance.cfg</code>.</p> </div> </div> <div class="sect2"> <h3 id="_unload_clients_with_delete_py">2.46. unload_clients_with_delete.py</h3> <div class="paragraph"> <p>This script will unload clients that have not been accessed since the number of weeks specified in the <code>maintenance.cfg</code> file.</p> </div> <div class="paragraph"> <p>This version of unload clients will delete clients with exclusive checkouts since unload will not unload those clients.</p> </div> <div class="paragraph"> <p>It also deletes shelves from the clients to be deleted since delete will not delete a client with a shelf.</p> </div> </div> <div class="sect2"> <h3 id="_unload_labels_py">2.47. unload_labels.py</h3> <div class="paragraph"> <p>This script will unload labels that have not been accessed since the number of weeks specified in the <code>maintenance.cfg</code> entry.</p> </div> </div> </div> </div> </div> <div id="footer"> <div id="footer-text"> Version v2019.3<br> Last updated 2020-11-05 15:10:02 -0500 </div> </div> </body> </html>
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 27246 | Jessie Fernandez | Branch for Jessie Fernandez | ||
//guest/perforce_software/sdp/dev/Unsupported/doc/Unsupported_SDP.html | |||||
#3 | 26885 | C. Thomas Tyler |
Removed obsolete README.txt file, and put useful bits of its content into Unsupported_SDP.adoc. Removed excess general SDP content from Unsupported_SDP.adoc. Enhanced Makefile for AsciiDocc to support building individual targets, and also incrementally build only what's needed. |
||
#2 | 26851 | Robert Cowham |
Fix typo in tmpfs /etc/fstab entry which stopped it working in the doc. Mention in pre-requisites for failover and failover guide the need to review OS Config for your failover server. Document Ubuntu 2020.04 LTS and CentOS/RHEL 8 support. Note performance has been observed to be better with CentOS. Document pull.sh and submit.sh in main SDP guide (remove from Unsupported doc). Update comments in triggers to reflect that they are reference implementations, not just examples. No code change. |
||
#1 | 26681 | Robert Cowham |
Removing Deprecated folder - if people want it they can look at past history! All functions have been replaced with standard functionality such as built in LDAP, or default change type. Documentation added for the contents of Unsupported folder. Changes to scripts/triggers are usually to insert tags for inclusion in ASCII Doctor docs. |