In `server.json` one can have a definition for lif...
# box-products
e
In
server.json
one can have a definition for lifecycle events, like
Copy code
"scripts":{
        "onServerInstall":"cfpm install mail"
}
How do I add more than one under the
onServerInstall
? For example I need to run the above and also a custom registered module as a task runner
mysites copyConfig app=myapp
.
b
@evagoras
Copy code
"scripts":{
       "onServerInstall" : "command; another-commmand; as; many; as; you; like"
}
Whatever you put in there is passed to CommandBox's shell just as though you had typed it directly
So all the tricks for running more than one command (
;
,
&&
, and
||
) apply here
πŸ‘ 1
e
Got it. Thanks, let me try that.
And if the readability of that is too poor, you can always refactor multiple commands out into a recipe and just run the recipe.
An interesting feature enhancement here would be to allow an array as well as a string, which would basically just make multiple commands more readable in the JSON. πŸ€”
πŸ‘πŸ» 1
e
Yes, I had actually tried that lol
🧠 1
b
Copy code
"scripts":{
       "onServerInstall" : [
          "command",
           "another-commmand",
           "as",
           "many",
           "as",
           "you",
           "like"
        ]
}
☝️ You should put in a ticket for this.
It's an easy enhancement and backwards compat
I like it
e
I don’t know how to edit the commandbox core otherwise I could do a PR for it
b
You just edit the CFC files and then run CommandBox again πŸ™‚
Just like any CF app
e
Ok, I can take a look at it.
After editing code in either of those files, you can run the
Copy code
reload
command and then the changes will be picked up. It's like the
fwreinit=1
for ColdBox
There's literally just a bunch of interceptors listening to all the core interception points that apply and then running the scripts if they exist
e
You make it sound easy. I will take a stab at it.
πŸ‘ 1
b
It is pretty easy πŸ™‚ I actually just have the CommandBox source code checked out locally in Git and I symlink the entire
cfml
folder over to my CommandBox home. That way I can hack on the core and then just commit when I'm done
e
@bdw429s I had a deeper look into this today. I realized that you probably meant to change the PackageManager.cfc because you thought I needed the scripts as an array in the
box.json
file. Instead, I needed it in the
server.json
. I created a ticket for it:https://ortussolutions.atlassian.net/browse/COMMANDBOX-1591 I also have the code working locally, but I have been trying to push my branch to github and I keep getting auth failures. It's the first time pushing to the repo so please bear with me!
Copy code
c:\dev\cf\commandbox>git push --set-upstream origin COMMANDBOX-1591-allow-the-server-scripts-in-server-json-to-use-an-array-for-each-key
remote: Permission to Ortus-Solutions/commandbox.git denied to evagoras.
fatal: unable to access '<https://github.com/Ortus-Solutions/commandbox.git/>': The requested URL returned error: 403
b
I intended for both to change πŸ™‚
e
ah right πŸ™‚
b
You need to push your branch to your own forked repo
Then you create a pull from your repo to mine
e
I thought I had it finished, now you've added the box.json change as well LOL. Oh well, let me check that one out as well.
b
To be fair, I "added" the
box.json
change yesterday when we very first talked about this πŸ™‚ https://cfml.slack.com/archives/C06TSRXCJ/p1685471732684909?thread_ts=1685460406.193339&amp;cid=C06TSRXCJ
e
https://github.com/Ortus-Solutions/commandbox/compare/development...evagoras:comma[…]BOX-1591--allow-scripts-key-to-use-array-in-server-and-box-json Where would that
chainScripts
method fit better as a common util, so I can use it in the PackageService as well?
b
I would actually do it differently
Loop over and run then individually
The problem with adding
;
is if someone has
Copy code
"onSomething" : [
  "!npm install",
  "install"
]
then that becomes
Copy code
!npm install; install
which will not work as EVERYTHING after the
!
will get 'eaten' by the
run
command and passed to the native shell
It will pass
npm install; install
to the native shell, which isn't what you want
e
right, ok
b
If the script is a string, then make it an array
Copy code
if( isSimpleValue( thisScript ) ) {
  thisScript = [ thisScript ];
}
and then always loop
You'll need to do that logic to BOTH the server.json and the server defaults. Then you can merge the arrays.
The joys of having multiple places all config can come from πŸ™‚
And by server defaults, I mean
Copy code
config set server.defaults.scripts.onServerStart="echo 'I run for every server ever!'"
In addition to:
Copy code
server set scripts.onServerStart="echo 'I only run for this server'"
e
I think I got misdirected by seeing that it was appending the server default script to the user script by adding an
;
here so I figured I could do the same.
Copy code
getDefaultServerJSON().scripts.each( (k,v)=>{
	// Append existing scripts
	if( serverJSONScripts.keyExists( k ) ) {
		serverJSONScripts[ k ] &= '; ' & v;
	// Merge missing ones
	} else {
		serverJSONScripts[ k ] = v;
	}
} );
I will take another look and try it like you suggest, but not today πŸ™‚
b
Yeah, I don't know why I even bothered looping over all the scripts, when all I really care about is the script name being passed in.
e
ok yes, i was wondering about that too!
b
Honestly, it can be reduced to this I think
Copy code
// get server.json script, defaulting to empty array
var serverJSONScripts = serverJSONScripts[ scriptName ] ?: [];
// get server default script, defaulting to empty array
var serverDefaultScripts = getDefaultServerJSON().scripts[ scriptName ] ?: [];

// force both to be an array, if not already
if( isSimpleValue( serverJSONScripts ) ) {
  serverJSONScripts = [ serverJSONScripts ];
}
if( isSimpleValue( serverDefaultScripts ) ) {
  serverDefaultScripts = [ serverDefaultScripts ];
}
// Combine both arrays
var totalScripts = serverJSONScripts.append( serverDefaultScripts , true )

// Only set up env vars, if there's something to run
if( totalScripts.len() && systemSettings.getAllEnvironments().len() > 1 ) {
  systemSettings.setDeepSystemSettings( interceptData );
}
totalScripts.each( (thisScript)=>{

				// Run preXXX package script
				runScript( 'pre#scriptName#', arguments.directory, true, interceptData );

				consoleLogger.debug( '.' );
				consoleLogger.warn( 'Running server script [#arguments.scriptName#].' );
				consoleLogger.debug( '> ' & thisScript );

				// Normally the shell retains the previous exit code, but in this case
				// it's important for us to know if the scripts return a failing exit code without throwing an exception
				shell.setExitCode( 0 );

				// ... then run the script! (in the context of the package's working directory)
				var previousCWD = shell.pwd();
				<http://shell.cd|shell.cd>( arguments.directory );
				shell.callCommand( thisScript );
				<http://shell.cd|shell.cd>( previousCWD );

				// If the script ran "exit"
				if( !shell.getKeepRunning() ) {
					// Just kidding, the shell can stay....
					shell.setKeepRunning( true );
				}

				if( shell.getExitCode() != 0 ) {
					throw( message='Server script returned failing exit code (#shell.getExitCode()#)', detail='Failing script: #arguments.scriptName#', type="commandException", errorCode=shell.getExitCode() );
				}

				// Run postXXX package script
				runScript( 'post#scriptName#', arguments.directory, true, interceptData );
} )
if( !arguments.ignoreMissing && !totalScripts.len() ) {
			consoleLogger.error( 'The script [#arguments.scriptName#] does not exist in this server.' );
}
Some of those if statements could be combined if you really wanted. There's a few ways this function gets called. For ex, if you call
Copy code
run-script foo
then we output an error if the
foo
script didn't exist, otherwise we ignore it.
But everything inside the keyExists check just becomes a loop
e
@bdw429s PR raised. I am sure it will need some work, please bear with my first commandbox PR. https://github.com/Ortus-Solutions/commandbox/pull/328
b
Awesome!