In config.yml, I define my collections like this:
collections:
music:
output: false
dancing:
output: false
The problem is I will have lots of collections and they will clutter my root Jekyll folder.
Is there a way to group all the collections into a folder, named for example, _collections?
So, I would have:
_collections
_dancing
_music
....
This is now possible (I'm running Jekyll 3.7.2, I'm not sure in which version this was implemented).
Here's how to do it: In your _config.yml you can define your collections as well as the folder for your collections. Let's take a look at an example on a client site I'm working on:
collections:
events:
output: true
work:
output: true
jobs:
output: true
cases:
output: true
permalink: /work/:name
collections_dir: pages
The collections_dir: [your_folder_here] will tell Jekyll to look into that folder for collections. My folder structure in development is as follows:
pages/
...
_events/
_work/
_jobs/
_cases/
And in the compiled site it's as follows:
...
events/
jobs/
work/ (contains both "work" and "cases" collections)
One thing also that wasn't asked, but I found to be useful, was that you're able to output different collections into a same folder. In my case I had a client website on which there were two types of work samples: client cases and general examples. We wanted to separate them for better maintenance but also show them in the same folder. To achieve this you can simply define a permalink for the collection. In our case we put permalink for the cases to appear in the work folder (permalink: /work/:name).
Hope this helps!
This is also present in the Jekyll documentation
Answer is no. Your collections folder must be at the root of your root folder.
Even if you name you create a collection in _collections/_music folder, and set it up like this :
collections:
collections/_music folder:
output: true
Jekyll ends up looking for your collection in _collections_music folder (without any slash) because of path sanitize process.
See jekyll code in collection.rb, site.rb and jekyll.rb
This is now possible since this issue was merged.
User configures as:
collections_dir: my_collections
Then we look in my_collections/_pizza for the pizza collection, and my_collections/_lasagna for the lasagna collection.
Nothing prevents you to use subfolders in your collection.
(Note: this is not an answer to your question but a possible workaround)
So as a workaround you could have just one collection: say _arts for example and organize your folders like:
_arts
dancing
music
concerts
.....
to list them separately you can use:
a FrontMatter variable in your files category: dancing when you have a output and check this for a dancing only list for example.
{% for project in site.arts %}
{% if project.category == 'dancing' %}
....
{% endif %}
{% endfor %}
or check the path when you have no output for the collection
{% for project in site.arts %}
{% if project.url contains 'dancing' %}
....
{% endif %}
{% endfor %}
This could slowdown your build if you have hundreds and hundreds of items inside.
Related
I'm new to using Hugo and Go Templates. How can I access a variable from a partial file that is defined in base file using Hugo?
For eg: I have an index.html file which contains code that reads the data stored in the events.json file in the data directory and stores it in a variable. How can I access that variable from another file?
index.html
{{ $events := .Site.Data.events }}
{{ partial "people" . }}
people.html
// access the events variable from the index.html
{{ $events }}
I really hope this makes sense. I can try and clarify more if needed.
0.15 introduced a map that can be used for this.
index.html
{{ $events := .Site.Data.events }}
{{ partial "people" (dict "events" $events) }}
people.html
// access the events variable from the index.html
{{ .events }}
You could use the dict func:
{{ partial "people" (dict "page" . "events" $events) }}
You will then address them like {{ .page.someVar }} and {{ .events.someVar }} in the partial.
An alternative in your case could maybe, in the partial (as the previous poster said), address the .Site.Data.events directly from the partial.
According to Hugo documentation:
... partial calls receive two parameters.
The first is the name of the partial and determines the file location to be read.
The second is the variables to be passed down to the partial.
This means that the partial will only be able to access those variables. It is isolated and has no access to the outer scope.
This means, the events variable is outside of the scope of people.html. Your people.html cannot "see" it. One solution would be pass it down, like:
{{ partial "people" . $events }}
If it does not work, try different notation ($ vs. .).
If that does not work, you can always call your data file again, without variable, just like in the examples, that is, use {{ .Site.Data.events }} in your people.html partial.
Let me know in the comments how it goes, I'll try to improve my answer if necessary. I know it's a pain to get out of Hugo boundaries into Go territory :)
I created a complex state for API service, it involves git checkouts, python venv, uwsgi, nginx, etc etc. It works fine.
Now I would like to turn it into a template and execute it several times per minion, with variables supplied from pillar - i.e something like.
{% for apiserver in pillar.apiservers %}
include apiserver_template.sls, locals: apiserver.config
{% endfor %}
where apiserver_template will work with context supplied to it, with apiserver.config having all config data for each API instance. I know syntax is wrong but hopefully I am communicating the idea - ideally, something like executing ruby partials with supplying local variables.
How is it done properly in saltland?
It sounds to me like Jinja Macro is something you want to use for this. You can find more information about usage here: https://docs.saltstack.com/en/2015.8/topics/development/conventions/formulas.html#jinja-macros
In short what you will have in your case may look like:
{% macro api_server(git_repo, python_venv_path, python_venv_requirements) %}
{{python_venv_path}}:
virtualenv.managed:
- system_site_packages: False
- requirements: salt://{{python_venv_requirements}}
{{git_repo}}:
git.latest:
- name: {{git_repo}}
{% endmacro %}
Assuming you have a pillar apiservers where each api server has git_repo, python_venv_path and python_venv_requirements values, you can use the macro like this:
{% for server in salt.pillar.get('apiservers', []) %}
{{ api_server(server['git_repo'], server['python_venv_path'], server['python_venv_requirements']) }}
{% endfor %}
If you want - you can also put a macro in a separate state file and then import a marco as a regular salt resource.
Please also not that instead of pillar.apiservers I used salt.pillar.get('apiservers', []). This is a safer way to get data from pillar. If for some reason a pillar is unavailable - the later code will result in empty dict instead of failure in first case.
I'm having trouble using the salt mine in a pillar to dynamically create a list of hosts based on a grain value match. I don't get any error, I get no output for all hosts. Actually, I can't get any output for mine from the pillar at all even when using the example from the salt docs. I know it isn't an issue with my top file, because I can access all of the other pillar values. My test minion's mine.interval is set to 5. I've refreshed pillar data, and ran mine.update.
Here's an example of my pillar:
mine_functions:
network.ip_addrs: []
grains.item:
- host
- role
My template file that access the mine functions:
#I know this is writing the same list for each match, I'm just doing this for testing and I'll concat the results into a string when I know it works:
{% for host in salt['mine.get']('roles:web', 'grains.items:host', expr_form='grain') | dictsort() %}
serverList= {{ host }}
{% endfor %}
Output from CLI:
salt "server.domain.com" mine.get "*" "*"
server.domain.com:
----------
How do I get this to work? I get no errors, no output, it just runs smoothly, but nothing is written in the file and I get nothing from the command line. My goal here is to be able to dynamically build a list of servers that match a specific grain to set a configuration value in a config template. Am I down the wrong path here, is there a better way?
I'd recommend using mine.get directly in your sls file to get that list of hosts. I don't think there's any need to pass that through pillar data.
#Utah_Dave, thanks so much for the help both here and in IRC.
Posting this as an answer so anyone else searching for this gets a good example...
pillar:
mine_functions:
grains.items: []
template file:
{% set ft_hosts = [] %}
{% for grain_vals in salt['mine.get']('role:ps:ft:True', 'grains.items', expr_form='grain').items() %}
{% do ft_hosts.append(grain_vals[1]['host']) %}
{% endfor %}
ft.ps.server.hosts={{ ft_hosts|join('|') }}
I'm trying to have salt automatically deploy files to users' home directory (after creating them). The directory structure (under file_roots) is as follows:
users/
init.sls
user_list.jinja
files/
userX/
some_dir_I_want_deployed_to_userX_homedir/
some_script_I_want_deployed.sh
etc
user_list.jinja has a list of users. The idea is that if there's a directory for userX under files/, that directory subtree should be deployed to userX's home. However, I'd rather not have to create empty directories for userY etc if they don't have anything to deploy, so I'm trying to test existence of the directory to avoid an error.
Here's the relevant excerpt from users/init.sls:
{% from "users/user_list.jinja" import users with context %}
{% for name, user in users.items() %}
{% set files_path = '{0}/files/{1}'.format(salt['file.dirname'](tplpath), name) %}
{% if salt['file.directory_exists'](files_path) %}
{{ user.home }}:
file.recurse:
- source: salt://users/files/{{ name }}
- user: {{ name }}
- group: {{ name }}
{% endif %}
{% endfor %}
With a fair bit of debugging (which was rather necessary to figure out the above), I've worked out that this is a chicken-and-egg situation, namely:
The file.directory_exists test is run on the minion (that's fair)
The salt file-server seems to have an optimization whereby it only deploys to minion (to their local cache under /var/cache/salt/minion/file) items which are referenced in states (more likely, the minions only request stuff which they see referenced).
So unless the directory subtree for users/files/userX already exists on the minion, file.directory_exists returns False, which means the entire portion which starts with {{ user.home }} is suppressed during Jinja rendering; hence it's not referenced and the copy (to the minion's local cache) never occurs.
If I manually create an empty directory structure for users/files/userX on the minion, everything starts to work. This tells me my theory is at least partially correct.
I can "feel" I'm doing smth wrong here (the whole thing feels too procedural). What's the best approach to achieve this? The requirement itself doesn't seem too far-fetched.
The more salt-ish way to do this is to have some data in pillar data and check for the existence of that key. Something like user.enabled. But, that would require you to keep settings in 2 places, in pillar and in the file_roots.
You don't want to check for the existence of the directory on the minion server, you want to check for the existence of the file in your file roots.
Unfortunately, I don't think it's possible to check for the existence of a file under salt:// scheme. If I'm wrong, then all you have to do is replace your check for directory existence with the syntax to check for file_root file existence.
The more salt-ish approach is to define which users are enabled/disabled on each machine in pillar data, and use the user module to add them to the system.
https://github.com/saltstack-formulas/users-formula
You can add to the standard pillar that's given with the standard users formula and put a key that says to sync files
#pillar
users:
ausername:
fullname: A User
syncfiles: True
busername:
fullname: B User
syncfiles: False
#state
{% for name, user in pillar.get('users', {}).items() if user.absent is not defined or not user.absent %}
{% if user.syncfiles %}
/home/{{ user.username }}:
file.recurse:
- source: salt://users/files/{{ user.username }}
- user: {{ user.username }}
{% if user.prime_group %}
- group: {{ user.prime_group.name }}
{% endif %}
{% endif %}
{% endfor %}
Actually, the standard users-formula already handles pre-populating with files. I would just use that formula. I know it's keeping track of data in 2 places, but you get the bonus of leveraging an already built state file.
I'm making a Jekyll site destinated for GitHub Pages, and pre-testing it on local Windows 8.1 PC with Jekyll 2.5.3, Ruby 2.0.0p598 (2014-11-13) [x64-mingw32] and Gem 2.0.14.
In Jekyll, I have a collection named albums.
Directory structure for _albums is similar to the following:
_albums
foo.md
bar.md
baz.md
Each file has Front Matter containing values such as title, description etc.
In some site page I'm making a Liquid loop such as following:
{% for album in site.albums %}
/* need to get album name here */
{% endfor %}
The question is:
How can I get a bare name for collection item — like foo or bar (without extension, containing folders etc.)
Jekyll documentation says something about name variable, but album variable only has following properties: output, content, path, relative_path, url, collection (and also title and description I set in Front Matter)
album.path has value similar to: d:/repo/<project_name>/_albums/foo.md
A workaround I use for now is:
{% assign album_name = album.path | split:"/" | last | split:"." | first %}
Could you please suggest any better way?
Thank you in advance.