<!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.23"> <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 the following line when using as a 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"; */ html{font-family:sans-serif;-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} b,strong{font-weight:bold} abbr{font-size:.9em} abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none} dfn{font-style:italic} hr{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} audio,video{display:inline-block} audio:not([controls]){display:none;height: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]{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{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;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;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{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} 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{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} ul.circle{list-style-type:circle} ul.disc{list-style-type:disc} ul.square{list-style-type:square} ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit} 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} blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} 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:1px solid #dedede;word-wrap:normal} 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{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;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;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;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:0 auto;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{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:flex;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} body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px} #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:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;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:hsla(0,0%,100%,.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{margin-left:1.25rem} details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent} details>summary::-webkit-details-marker{display:none} details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)} details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)} details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem} .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)} .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);word-wrap:anywhere} .admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} .exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px} .sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px} .sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} .exampleblock>.content>:first-child,.sidebarblock>.content>:first-child{margin-top:0} .exampleblock>.content>:last-child,.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,.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{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;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,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none} pre.pygments span.linenos{display:inline-block;margin-right:.75em} .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-serif;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;font-size:.85rem;text-align:left;margin-right:0} p.tableblock:last-child{margin-bottom:0} td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere} 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{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} 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} li>p:empty:only-child::before{content:"";display:inline-block} ul.checklist>li>p:first-child{margin-left:-1em} 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:flex;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} td.hdlist2{word-wrap:anywhere} .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:4px solid #fff;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,#footnotes .footnote a:first-of-type: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} 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);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,summary{letter-spacing:-.01em} p strong,td.content strong,div.footnote strong{letter-spacing:-.005em} p,blockquote,dt,td.content,td.hdlist1,span.alt,summary{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;box-shadow:0 1px 4px #e0e0dc} .print-only{display:none!important} @page{margin:1.25cm .75cm} @media print{*{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]{border-bottom:1px dotted} 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 amzn-kf8,print{#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-helix-core@perforce.com">consulting-helix-core@perforce.com</a></span><br> <span id="revnumber">version v2024.1.1,</span> <span id="revdate">2024-11-20</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="#_changeid_pl">1.4.27. changeid.pl</a></li> <li><a href="#_command_block_py">1.4.28. command_block.py</a></li> <li><a href="#_dictionary">1.4.29. dictionary</a></li> <li><a href="#_externalcopy_txt">1.4.30. externalcopy.txt</a></li> <li><a href="#_otpauthcheck_py">1.4.31. otpauthcheck.py</a></li> <li><a href="#_otpkeygen_py">1.4.32. otpkeygen.py</a></li> <li><a href="#_rad_authcheck_py">1.4.33. rad_authcheck.py</a></li> <li><a href="#_radtest_py">1.4.34. radtest.py</a></li> <li><a href="#_submit_form_1_py">1.4.35. submit_form_1.py</a></li> <li><a href="#_submit_form_1_in_py">1.4.36. 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-helix-core@perforce.com">consulting-helix-core@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 directories 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 This trigger ensures users are not adding new files (directly or by branching) which only differ in case (either filename or a directory element of their path) from existing depot paths. It is useful for both case-sensitive and case-insensitive servers, although most used for the former. Example 1: Typical usage from the Helix Core server triggers table: Triggers: CheckCaseTrigger change-submit //... "/usr/bin/python3 /p4/common/bin/triggers/CheckCaseTrigger.py %changelist% myuser=%user%" 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' BYPASS LOGIC: By default, this trigger can be bypassed by any user by adding the token BYPASS_CASE_CHECK to the changelist description. Specify 'allowbypass=no' on the command line to disable the ability to bypass this trigger As an exception, if the user is 'git-fusion-user', the case check is always bypassed if 'myuser' is defined. DEPENDENCIES: This trigger requires P4Triggers.py and the P4Python API. SEE ALSO: See 'p4 help triggers'. """ """CaseCheckTrigger is a subclass of P4Trigger. Use this trigger to ensure that your depot does not contain two filenames or directories that only differ in case. Having files with different case spelling will cause problems in mixed environments where both case-insensitive clients like Windows and case- sensitive clients like UNIX access the same server. """</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 enforcement 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>The <code>JobIncrement.pl</code> trigger enables increment of jobs with custom name, e.g PROJX-123, even without integration with an external defect tracking system.</p> </div> <div class="paragraph"> <p>See: <a href="JobIncrement.html">JobIncrement.html</a>.</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="_changeid_pl">1.4.27. changeid.pl</h4> <div class="paragraph"> <p>The <code>changeid.pl</code> script is a sample Perforce server trigger for use in conjunction with the Perforce Helix Defect Tracking Gateway (P4DTG) replication to jobs.</p> </div> <div class="paragraph"> <p>If you intend to use this script, it shoudl be copied from <code>/p4/sdp/Unsupported/Samples/triggers/changeid.pl</code> to <code>/p4/common/site/bin/triggers/changeid.pl</code>. The directory <code>/p4/common/site/bin/triggers</code> may need to be created first. Any modifications to the script (per below) should be made to the copied file.</p> </div> <div class="paragraph"> <p>This script can optionally:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Prevent creation of new jobs in Perforce by anyone other than the replication user.</p> </li> <li> <p>Prevent modification of read-only fields of jobs in Perforce by anyone other than the replication user.</p> </li> <li> <p>Create newly mirrored jobs using the same name as that in the defect tracker.</p> </li> </ol> </div> <div class="paragraph"> <p>To install, add a line to your triggers table like the following (on UNIX/Linux):</p> </div> <div class="literalblock"> <div class="content"> <pre>jobId form-in job "/p4/common/site/bin/triggers/changeid.pl %user% %formfile%"</pre> </div> </div> <div class="paragraph"> <p>Make the file executable, and change the path to the Perl interpreter on the first line of the script if needed. Then set the configuration in the script itself. To configure, read and modify lines up to the comment that reads "END OF CONFIGURATION BLOCK" in the <code>changeid.pl</code> script. You may also need to modify the definition of which fields constitute a new job based on your jobspec. This is in the <code>allowed_job()</code> function.</p> </div> </div> <div class="sect3"> <h4 id="_command_block_py">1.4.28. 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.29. 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.33, “rad_authcheck.py”</a></p> </div> </div> <div class="sect3"> <h4 id="_externalcopy_txt">1.4.30. 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.31. 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.32. 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.31, “otpauthcheck.py”</a></p> </div> </div> <div class="sect3"> <h4 id="_rad_authcheck_py">1.4.33. 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.34. 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.35. 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.36. 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 table 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 sophisticated branching and merging capability. With this sophistication came complexity, 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 situation 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 occurring 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 relationship, 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 Display 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 library 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 detect <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 v2024.1.1<br> Last updated 2024-11-20 18:56:10 -0500 </div> </div> </body> </html>
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#13 | 30913 | C. Thomas Tyler | Regnerated docs for release. | ||
#12 | 30835 | C. Thomas Tyler |
Adapted Server Spec Naming Standard section detailing the ServerID of the commit server to the defacto standard already used in HRA. Changed from: {commit|master}[.<SDPInstance>[.<OrgName>]] to: {commit|master}[.<OrgName>[.<SDPInstance>]] Various typo fixes and minor changes in SDP Guide. Updated consulting email address (now consulting-helix-core@perforce.com) in various files. |
||
#11 | 30385 | C. Thomas Tyler | Regnerated docs for release. | ||
#10 | 30085 | C. Thomas Tyler | SDP-ified P4DTG changeid.pl script rescued from P4DTG-SDK, added basic docs (just copied from the script and tweaked). | ||
#9 | 30041 | C. Thomas Tyler | Regenerated docs. | ||
#8 | 30000 | C. Thomas Tyler |
Refined Release Notes and top-level README.md file in preparation for coming 2023.2 release. Adjusted Makefile in doc directory to also generate top-level README.html from top-level README.md file so that the HTML file is reliably updated in the SDP release process. Updated :revnumber: and :revdate: docs in AsciiDoc files to indicate that the are still current. Avoiding regen of ReleaseNotes.pdf binary file since that will need at least one more update before shipping SDP 2023.2. |
||
#7 | 29608 | C. Thomas Tyler | Doc updates as part of release cycle. | ||
#6 | 27727 | C. Thomas Tyler |
Refined docs for Unsupported scripts. Enhanced Unsupported doc Makefile to handle pod2html doc. Re-generated PDF and HTML docs. |
||
#5 | 27726 | C. Thomas Tyler |
Generated HTML and PDF from adoc for Unsupported folder. Corrected Makefile so 'make clean' also removes *.html files. Added missing doc tags in CheckCaseTrigger.py. |
||
#4 | 27722 | C. Thomas Tyler |
Refinements to @27712: * Resolved one out-of-date file (verify_sdp.sh). * Added missing adoc file for which HTML file had a change (WorkflowEnforcementTriggers.adoc). * Updated revdate/revnumber in *.adoc files. * Additional content updates in Server/Unix/p4/common/etc/cron.d/ReadMe.md. * Bumped version numbers on scripts with Version= def'n. * Generated HTML, PDF, and doc/gen files: - Most HTML and all PDF are generated using Makefiles that call an AsciiDoc utility. - HTML for Perl scripts is generated with pod2html. - doc/gen/*.man.txt files are generated with .../tools/gen_script_man_pages.sh. #review-27712 |
||
#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. |