How do nested variables within the .env file work in CodeIgniter 4 - codeigniter

Under the "Nesting Variables" section in Codeigniter4 site:
"To save on typing, you can reuse variables that you’ve already specified in the file by wrapping the variable name within ${...}"
link to CI nesting Variables section
example in the documentation:
BASE_DIR="/var/webroot/project-root"
CACHE_DIR="${BASE_DIR}/cache"
TMP_DIR="${BASE_DIR}/tmp"
I was trying to use the following
app.baseURL = 'http://localhost:8080/'
google.redirect = ${app.baseURL}Google
However, it's assigning it as a literal when print_r($_ENV)
[google.redirect] => ${app.baseURL}Google
I've tried using non-namespaced keys including BASE_DIR (per the example) and it keeps printing as a literal.
What's strange - When I use the following:
CI_ENVIRONMENT = development
google.redirect = ${CI_ENVIRONMENT}Google
The result when print_r is:
[CI_ENVIRONMENT] => development
[google.redirect] => developmentGoogle
My question is - What am I doing incorrectly and/or how should these be set/used correctly?
According to the documentation, I should be able to use any key within the .env file that was already assigned using
${somekeyinthisfile}

After a bit of looking, there is a more recent file up at
https://github.com/codeigniter4/CodeIgniter4/blob/develop/system/Config/DotEnv.php
with all the "other" changes...
This was a Bug Fix. So get that file and you will be good to go.
I am pretty sure that the intention wasn't to allow app.xxx settings to be used as variables as the documentation clearly shows, by not
showing them being used. ( yes its 6am now ...)
BUT it is your code to do with as you please...So if you want to use app.xxx as variables...
The Only Thing missing is the DOT (.) in the regex
If you look on Line 272 - system/Config/DotEnv.php inside method resolveNestedVariables() and add a . (dot) into the regex, that will make all your app.things work.
$value = preg_replace_callback(
'/\${([a-zA-Z0-9_.]+)}/',
function ($matchedPatterns) use ($loader) {
I have added a dot (.) at the end of the [a-zA-Z0-9_
So
'/\${([a-zA-Z0-9_]+)}/',
becomes
'/\${([a-zA-Z0-9_.]+)}/',

Related

Fetch variable from yaml in puppet manifest

I'm doing one project for puppet, however currently stuck in one logic.
Thus, want to know can we fetch variable from .yaml, .json or plain text file in puppet manifest file.
For example,
My puppet manifest want to create user but the variable exist in the .yaml or any configuration file, hence need to fetch the varibale from the outside file. The puppet manifest also can do looping if it exist multiple users in .yaml file.
I read about hiera but let say we are not using hiera is there any possible way.
There are a number of ways you can do this using a combination of built-in and stdlib functions, at least for YAML and JSON.
Using the built-in file function and the parseyaml or parsejson stdlib functions:
Create a file at mymodule/files/myfile.yaml:
▶ cat files/myfile.yaml
---
foo: bar
Then in your manifests read it into a string and parse it:
$myhash = parseyaml(file('mymodule/myfile.yaml'))
notice($myhash)
That will output:
Notice: Scope(Class[mymodule]): {foo => bar}
Or, using the loadyaml or loadjson stdlib functions:
$myhash = loadyaml('/etc/puppet/data/myfile.yaml')
notice($myhash)
The problem with that approach is that you need to know the path to file on the Puppet master. Or, you could use a Puppet 6 deferred function and read the data from a file on the agent node.
(Whether or not you should do this is another matter entirely - hint: answer is you almost certainly should be using Hiera - but that isn't the question you asked.)

How to add code line number using structlog

Using python standard logging module, the line number for the originating log call can be added using: %(lineno)s.
How can this be accomplished using structlog?
EDIT:
Structlog version 21.5.0 introduced the CallsiteParameter processor, so this should be a much more straightforward process right now, as #vitvlkv's answer shows.
I had a similar need and I ended up creating a custom processor
I took a look to what structlog does to output the module and line number when it is told to "pretend" to format in a compatible mode with the logging library (meaning: when it's using a regular stdlib.LoggerFactory) and I found inspiration in that. The key were the following words...
By using structlog’s structlog.stdlib.LoggerFactory, it is also ensured that variables like function names and line numbers are expanded correctly in your log format.
... from this documentation page
The code seems to keep looking for execution frames until it finds one that is in a non logging-related module.
I have all the setup for structlog inside a module called my_libs.util.logger so I want to get the first frame that is NOT inside that module. In order to do that, I told it to add my logging-related my_libs.util.logger to those exclusions. That's what the additional_ignores in the code below does.
In the example I hardcoded the module's name ('my_libs.util.logger') in the exclusion list for clarity, but if you have a similar setup you'll probably be better off using __name__ instead. What this does is ignoring execution frames that exist because of the logging machinery in place. You can look at it as a way of ignoring calls that may have occurred as part of the process of actually logging the message. Or, otherwise said, calls that happened after the logging.info("Foo") that happened in the actual module/line that you do want to output.
Once it finds the right frame, extracting any kind of information (module name, function name, line number... ) is very easy, particularly using the inspect module. I chose to output the module name and the line number, but more fields could be added.
# file my_libs/util/logger.py
import inspect
from structlog._frames import _find_first_app_frame_and_name
def show_module_info_processor(logger, _, event_dict):
# If by any chance the record already contains a `modline` key,
# (very rare) move that into a 'modline_original' key
if 'modline' in event_dict:
event_dict['modline_original'] = event_dict['modline']
f, name = _find_first_app_frame_and_name(additional_ignores=[
"logging",
'my_libs.util.logger', # could just be __name__
])
if not f:
return event_dict
frameinfo = inspect.getframeinfo(f)
if not frameinfo:
return event_dict
module = inspect.getmodule(f)
if not module:
return event_dict
if frameinfo and module:
# The `if` above is probably redundant, since we already
# checked for frameinfo and module but... eh... paranoia.
event_dict['modline'] = '{}:{}'.format(
module.__name__,
frameinfo.lineno,
)
return event_dict
def setup_structlog(env=None):
# . . .
ch.setFormatter(logging.Formatter('%(message)s'))
logging.getLogger().handlers = [ch]
processors = [
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
# . . . more . . .
show_module_info_processor, # THIS!!!
structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S"),
structlog.processors.format_exc_info,
structlog.processors.StackInfoRenderer(),
# . . . more . . .
]
# . . . more . . .
structlog.configure_once(
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
context_class=structlog.threadlocal.wrap_dict(dict),
processors=processors,
)
This produces an output like:
server_1
| INFO [my_libs.hdfs] 2019-07-01 01:01:01 [info ] Initialized HDFS
[my_libs.hdfs] modline=my_libs.hdfs:31
According to official docs, you may add
structlog.configure(
processors=[
# ...
# Add callsite parameters.
structlog.processors.CallsiteParameterAdder(
[CallsiteParameter.FILENAME,
CallsiteParameter.FUNC_NAME,
CallsiteParameter.LINENO],
),
# ...
],
So, I guess there is no need to write a custom processor for this. It was hard to find in the official docs though.
Have a look at this answer to the more general question of how to get a line number.
https://stackoverflow.com/a/3056270/5909155
This cannot be bound to the logger with log.bind(...) because it has to be evaluated each time you log. Thus, you should add a key-value pair like this
logger.log(..., lineno=inspect.getframeinfo(inspect.currentframe()).lineno)
each time. Maybe wrap this in a function, though, like this: https://stackoverflow.com/a/20372465/5909155
Don't forget to
import inspect

Replacing variable value in ruby while setting the value using "set" command

I have a .properties files as below:
user:abcd
pwd:xyz
system:test
Next, I have a ruby script with Watir for browser automation. In this script, I have statements like
browser.text_field(:id => 'identifierId').set "#{user}:variable to be replaced by its value from .properties file".
Similarly, other values need to be replaced for "pwd" and "system".
I tried the solution per below posts:
Replace properties in one file from those in another in Ruby
However, "set" command is setting whatever has been paased as arguments to it instead of replacing the variable with its value.
Please help.
You have to read the information out of the file.
Most Watir users leverage yaml files for this.
config/properties.yml:
user: abcd
pwd: xyz
system: test
Then read the yaml file & parse your data:
properties = YAML.safe_load(IO.read('config/properties.yml'))
text_field = browser.text_field(id: 'identifierId')
text_field.set properties['user']
Alternately you can take a look at Cheezy's Fig Newton gem, which is designed to work with his Page Object gem

Use issue_closing_pattern variable to close multiple issues in gitlab

I'd like to have the ability to close multiple issues with one commit by referencing multiple issues with the default pattern ^([Cc]loses|[Ff]ixes) +#\d+a. I know that this will only affect fixes #number-patterns at the beginning of lines and that's what I want.
But I wasn't yet able to get it to work.
I'm currently using Gitlab 6.1, installed it according to the installation readme on github and didn't change anything other then the codesnippet below.
Here's what I tried:
First I changed in {gitlab-directory}/app/models/commit.rb the following (original code commented out):
def closes_issues project
md = safe_message.scan(/(?i)((\[)\s*(close|fix)(s|es|d|ed)*\s*#\d+\s*(\])|(\()\s*(close|fix)(s|es|d|ed)*\s*#\d+\s*(\)))/)
#md = issue_closing_regex.match(safe_message)
if md
extractor = Gitlab::ReferenceExtractor.new
md.each do |n|
extractor.analyze(n[0])
end
extractor.issues_for(project)
#extractor = Gitlab::ReferenceExtractor.new
#extractor.analyze(md[0])
#extractor.issues_for(project)
else
[]
end
end
But the regex used in this code snippet doesn't fit my needs and isn't really correct (e.g.: (fixs #123) and (closees #123) would both work).
After testing this codesnippet and confirming that this one works with patterns that match the regex used in the snippet, I tried to change the regex. At first, I tried to do this in the second line:
md safe_message.scan(/#{Gitlab.config.gitlab.issue_closing_pattern}/)
This one didn't work. I didn't found any error messages in log/unicorn.stderr.log so I tried to use the default regex from the config file directly without variable:
md safe_message.scan(/^([Cc]loses|[Ff]ixes) +#\d+a/)
But this one didn't work, too. Again, no error messages in log/unicorn.stderr.log.
How do I use the variable issue_closing_pattern from the config file as regex pattern in this code snippet?
If the regex you provide to the String#scan method contains capture groups, it returns an array of arrays containing the patterns matched by each group:
irb(main):014:0> regex = "^([Cc]loses|[Ff]ixes) +#\\d+"
=> "^([Cc]loses|[Ff]ixes) +#\\d+"
irb(main):017:0> safe_message = "foo\ncloses #1\nfixes #2\nbar"
=> "foo\ncloses #1\nfixes #2\nbar"
irb(main):018:0> safe_message.scan(/#{regex}/)
=> [["closes"], ["fixes"]]
Because the default regex has a capture group for just the "closes/fixes" bit, that's all the loop is seeing, and those strings don't contain the issue references! To fix it, just add a capture group around the entire pattern:
irb(main):019:0> regex = "^(([Cc]loses|[Ff]ixes) +#\\d+)"
=> "^(([Cc]loses|[Ff]ixes) +#\\d+)"
irb(main):020:0> safe_message.scan(/#{regex}/)
=> [["closes #1", "closes"], ["fixes #2", "fixes"]]

Reading in variables from a file in Ruby

Is there a way to read in a file of environment variables?
In bash I have a file env.sh that I can use
env.sh
foo="bar"
bash file
set -a
source env.sh
This would allow me to just use foo as if I had delcared it in the ruby script.
Also is it there a way to make sure that this file is unreadable so that passwords could be stored in this file?
It sounds like you should provide a file example for the user/admin to modify for their personal environment, and then populate the environment from that, while avoiding, perhaps, having that file with the sensitive information in a repository. Note: per file security is going to be addressed by where the file is located and your operating system, and server software.
If this is the case, then you can provide a file that holds a template of the kind of things that you would require from the administrator/user of the program you are configuring.
Ruby has the ENV constant that acts like a Hash and holds the environment of the shell you are using.
As an example, there is a file called environment.rb.sample that gets shared with anyone, publicly. It has instructions and holds the template that users can modify freely, with instructions to copy the file to environment.rb. The sample file looks like this:
# environment.rb.sample
# Copy this file to environment.rb and change the name and password to your credentials
ENV['temp_user_name'] = 'Foo Bar'
ENV['temp_password'] = 'Dazz Kezz
The file is then copied to this, perhaps:
# environment.rb
ENV['temp_user_name'] = 'Joe Admin'
ENV['temp_password'] = 'Super Secure Password'
The file that loads this and uses it is just a Ruby file that is freely modified by the user/administrator of the software, and looks like this and is also shared publicly.
# load_environment
require './environment'
puts ENV['temp_user_name']
puts ENV['temp_password']
This loads the file and uses the ENV that is a globally scoped constant for the application.
The file permissions are then managed by the user/administrator of the system and secured like any other sensitive information on their system. The sensitive file should also be listed in the repository's ignore mechanism. It should never be made public.
Yes, there is, and if for some bizzare, arcane reason you must use it, it's eval:
WARNING: Never use this on a user-supplied file
And, unless you have a very, very specific need, don't use it in production code.
eval(File.read("name_of_var_file"), binding)
If what you're really trying to do is write a configuration file, use YAML. A file like this:
config.yaml:
foo: "bar"
Can be accessed like this:
require 'yaml'
conf = YAML.loads(File.read("config.yaml"))
conf['foo'] #=> 'bar'
This is secure and manageable, not to mention standard practice.
As for making the file inaccessible, that is an operating system level problem that can't be solved without information on the environment, OS, setup, etc.
The purpose of a local variable is to be used temporally within a method definition or a block. Using it outside of such environments, particularly across files defeats the purpose of it. You should not need to do it, and Ruby does not have a simple way to do it.
If you are using variables correctly, and want to share variables between files, that should be other types of variables such as instance, class, or global variables. Or, for the purpose of setting environments, you should be using constants. Among them, global variables and constants can be written in a file, loaded in a different file, and be used.
file-a.rb
$foo = 1
FOO = 2
file-b.rb
load "file-a.rb"
$foo # => 1
FOO # => 2
As for instance and class variables, they belong to a class or an instance of it, so they should be defined in such environment. And you can reopen the same class within a different file, and load it in another file.
file-a.rb
class Bar
##foo = 1
def initialize; #foo = 2 end
end
file-b.rb
load "file-a.rb"
Bar.class_variable_get("##foo") # => 1
Bar.new.instance_variable_get("#foo") # => 2

Resources