Fix#5242
Do this by first supplying the `servicePath` in `~/lib/plugins/lib/validate.js` and `~/lib/plugins/print.js` in a consistent manner with the code in `~/lib/classes/PluginManager.js`. Then, provide a default of `process.cwd()` for `servicePath` in `getCacheFilePath` and `getServerlessConfigFile`.
If an override comes back as a variable containing a deep variable (e.g. `''${self:custom.${deep:1}, "fallback"}''`) or really any variable at all, it needs to be made a deep variable and paused for next iteration. Do this generally rather than only in the case of deep variables. See the test case for a circumstance that the prior fix didn't resolve. This more generalized handling improves the fix and solves a remaining issue we found locally.
Use the variable string from which the overwrite parameters were extracted as the replacement target (in the case of deep variable discovery) rather than the context property which represents the entire original value that the replacement is being waited upon. That context property can contain far more content than the overwrite string itself.
This avoids taking a deep value as a valid term within an overwrite
(defaulting) variable statement that will later resolve to undefined
but, as a deep variable string, is valid in immediate evaluation.
Fixes#5027
Add explanatory text:
```
This can result from latent connections but may represent a cyclic
variable dependency
```
Also, rather than messages such as:
```
Serverless Information ----------------------------------
##########################################################################################
Serverless Information ----------------------------------
# 0: 0 of 2 promises have settled
Serverless Information ----------------------------------
# 0: 2 unsettled promises:
Serverless Information ----------------------------------
# 0: foo waited on by: ${foo:}
Serverless Information ----------------------------------
# 0: foo waited on by: ${foo:}
Serverless Information ----------------------------------
##########################################################################################
```
Collate the message to read like:
```
Serverless Information ----------------------------------
##########################################################################################
# 0: 0 of 2 promises have settled
# 0: 2 unsettled promises:
# 0: foo waited on by: ${foo:}
# 0: foo waited on by: ${foo:}
# This can result from latent connections but may represent a cyclic
variable dependency
##########################################################################################
```
Use the variable syntax present in the given variable string, extracting it from the given string that may contain embedded custom variables (i.e. `${{self:var.${{self:var.foo}}.bar}}`).
Handle `self:service.[...]` references properly. The prior represents what the user typed into their configuration. In service, we move all this from `service` to `serviceObject`, therefore, by rewriting `self:service.[...]` to `self:serviceObject.[...]` user's will get the result they expect.
Use else if, given exclusivity of conditions
The framework's Service class modifies a user's service, mutating a given `service: { name: 'string' }` to `service: 'string'` but doesn't account for this in the variables system. This is the basis for #4744.
While working this area, I discovered that the Service class also does this to the provider, accepting `provider: 'aws'` and replacing it with `provider: { name: 'aws' }`, causing the natural `${self:provider}` to fail.
Catching either 'self:service.name' or 'self:provider' and replacing them with the mutated reference solves this user confusion.
Fix `print`
The print command is highly linked to the `Variables` and `Service` codebases, keep those in sync and leave reminders about the link. Made these explicit and separately implemented to avoid complexity.
Additionally, the print command re-populates an object with the *very similar* content as the previously pre-populated service (just not augmented as just mentioned). This can lead to cross contamination between the two. As such, all caches must be cleared per unique invocation of service/object/property population.
Add tests for some expected but previously unverified behaviors.
Clean pre-population
The previous implementation worked okay but was unnecessary and would have been a maintenance problem. Instead, just knock out the population of variables depending on those config dependent services and use the standard means of resolution.
Fix cyclic bug (resulting from running print against a self-referencing serverless.yml)
The caching of values could lead to a cyclic object remaining in the caches for variable population. This causes crashes and pain. Solved by the cache cleaning logic.
Simple oversight that deep variables can reference overwrites too. This fix acknowledges this oversight by using the standard logic for using overwrite instead of getValueFromSource in this case.