A tool to create deployment folders based on YAML configuration
Project description
DeployFolder
A tool to create deployment folders based on YAML configuration. This tool helps you create structured deployment folders by copying files from source locations, optionally renaming them, and creating empty folders as needed.
Features
- Create deployment folders with a specified structure
- Copy files from source to target paths
- Support for glob patterns (wildcards like *) in source file paths
- Optionally rename files during copying
- Support for placeholders in filenames and folder names
- Replace placeholders with values from a JSON file
- Create empty folders
- Generate files from Jinja2 templates (inline or from external files)
- Optionally zip the created folder
- Cross-platform compatibility (Windows and Linux)
Installation
Install from PyPI (Python 3.7+):
pip install deployfolder
Optional: with 7z support
pip install deployfolder[7z]
Dependencies: PyYAML (MIT) and Jinja2 (BSD-3-Clause). The optional [7z] extra uses py7zr (LGPL-2.1+); install it only if you need 7z archives. See the upstream projects for the full license texts.
Usage
Using the Command-line Interface
After installing the package:
deployfolder config.yaml [--values values.json]
Arguments
config.yaml: Path to the YAML configuration file (required)--values values.json: Path to the JSON values file for placeholder replacement (optional)--version: Show version information and exit
Configuration File Format
The YAML configuration file defines the structure of the deployment folder:
output_folder: "folder_name" # Optional, defaults to "deploy"
archive: true # Optional, defaults to false
files: # List of files to copy or folders to create
# Simple file copy (keeps original filename in root directory)
- "path/to/source/file.txt"
# Copy with original filename to a subdirectory
- source: "path/to/source/file.txt"
directory: "docs"
# Copy with original filename to a subdirectory with placeholder
- source: "path/to/source/file.txt"
directory: "{{ environment }}/docs"
# Copy with renaming
- source: "path/to/source/file.txt"
target: "new_name.txt"
# Copy with placeholders in target name
- source: "path/to/source/config.ini"
target: "{{ environment }}_config.ini"
# Create empty folder
- target: "logs/{{ environment }}"
templates: # List of templates to render
# Template with inline content
- content: |
# Generated Configuration
# Generated on: {{ date }}
[Application]
Name = {{ project_name }}
Environment = {{ environment }}
target: "config/generated_config.ini"
# Template from external file
- file: "path/to/template.txt"
target: "reports/{{ date }}_report.txt"
File Configuration Options
When configuring files in the YAML configuration, you have several options:
-
Simple string format: Just specify the source path. The file will be copied to the root of the output folder with its original filename.
- "path/to/source/file.txt"
You can also use glob patterns to match multiple files:
- "path/to/source/*.txt" # Matches all .txt files in the directory - "path/to/source/file?.txt" # Matches file1.txt, file2.txt, etc. - "path/to/source/[abc]*.txt" # Matches files starting with a, b, or c
-
Directory format: Specify both
sourceanddirectory. The file will be copied to the specified subdirectory while keeping its original filename.- source: "path/to/source/file.txt" directory: "docs"
Glob patterns are also supported in the source path:
- source: "path/to/source/*.txt" directory: "docs" # All matching files will be copied to the docs directory
-
Target format: Specify both
sourceandtarget. The file will be copied to the specified target path, which can include a new filename.- source: "path/to/source/file.txt" target: "new_name.txt"
When using glob patterns with a target, there are two behaviors:
# If target ends with / or \, it's treated as a directory - source: "path/to/source/*.txt" target: "text_files/" # All matching files will be copied to the text_files directory # If target doesn't end with / or \, only the first matching file will be used - source: "path/to/source/*.txt" target: "first_text_file.txt" # Only the first matching file will be copied and renamed
-
Empty folder: Specify only
target. An empty folder will be created at the specified path.- target: "logs/temp"
Template Configuration Options
When configuring templates in the YAML configuration, you have two main options:
-
Inline template content: Specify both
contentandtarget. The template content will be rendered and saved to the specified target path.- content: | # Generated Configuration # Generated on: {{ date }} [Application] Name = {{ project_name }} Environment = {{ environment }} target: "config/generated_config.ini"
-
External template file: Specify both
fileandtarget. The template file will be loaded, rendered, and saved to the specified target path.- file: "path/to/template.txt" target: "reports/{{ date }}_report.txt"
Templates support all Jinja2 features, including:
- Placeholders (e.g.,
{{ variable }}) - Conditionals (e.g.,
{% if condition %}...{% else %}...{% endif %}) - Loops (e.g.,
{% for item in items %}...{% endfor %}) - Filters (e.g.,
{{ variable | default('default value') }}) - And more advanced Jinja2 features
The target path can include placeholders that will be replaced with values from the JSON values file, just like with file targets.
Placeholders
Placeholders in the format {{ name }} can be used in:
- The output folder name
- Target file names
- Target folder names
- Directory paths
Placeholders are replaced with values from the JSON values file.
Nested JSON Support
The tool supports accessing nested JSON properties using dot notation in placeholders. For example:
{{ user.name }}- Accesses the "name" property inside the "user" object{{ database.host }}- Accesses the "host" property inside the "database" object{{ database.credentials.username }}- Accesses deeply nested properties
This allows for more structured and organized values files, especially for complex configurations.
Values File Format
The JSON values file provides values for placeholders:
{
"project_name": "MyProject",
"environment": "production",
"date": "2023-01-01"
}
Examples
Example Configuration (example_config.yaml)
output_folder: "{{ project_name }}_deploy"
archive: true
files:
# Simple file copy (keeps original filename in root directory)
- "C:/path/to/source/file1.txt"
# Using glob pattern to copy multiple files
- "C:/path/to/source/*.txt"
# Copy with original filename to a subdirectory
- source: "C:/path/to/source/file1.txt"
directory: "docs"
# Using glob pattern with directory
- source: "C:/path/to/source/test_*.txt"
directory: "test_files"
# Copy with original filename to a subdirectory with placeholder
- source: "C:/path/to/source/file1.txt"
directory: "{{ environment }}/docs"
# Copy with renaming
- source: "C:/path/to/source/file2.txt"
target: "renamed_file2.txt"
# Copy with placeholders in target name
- source: "C:/path/to/source/config.ini"
target: "{{ environment }}_config.ini"
# Copy to subfolder
- source: "C:/path/to/source/data.csv"
target: "data/{{ date }}_data.csv"
# Copy with nested JSON placeholders
- source: "C:/path/to/source/file2.txt"
target: "users/{{ user.name }}_file.txt"
# Copy with deeply nested JSON placeholders
- source: "C:/path/to/source/config.ini"
target: "db/{{ database.host }}/{{ database.credentials.username }}_config.ini"
# Create empty folder with nested JSON placeholder
- target: "logs/{{ user.role }}"
# Create empty folder (simple)
- target: "temp"
templates:
# Template with inline content
- content: |
# Generated Configuration
# Generated on: {{ date }}
[Application]
Name = {{ project_name }}
Environment = {{ environment }}
[User]
Name = {{ user.name }}
Role = {{ user.role }}
target: "config/generated_config.ini"
# Template from external file
- file: "C:/path/to/templates/report_template.txt"
target: "reports/{{ date }}_report.txt"
# Template with Jinja2 conditionals
- content: |
<!DOCTYPE html>
<html>
<head>
<title>{{ project_name }} - Environment Info</title>
</head>
<body>
<h1>Environment Information</h1>
{% if environment == 'production' %}
<p class="warning">This is a PRODUCTION environment. Be careful!</p>
{% else %}
<p>This is a {{ environment }} environment.</p>
{% endif %}
<h2>Database Information</h2>
<ul>
{% for key, value in database.items() if key != 'credentials' %}
<li>{{ key }}: {{ value }}</li>
{% endfor %}
</ul>
</body>
</html>
target: "docs/environment.html"
Example Values (example_values.json)
{
"project_name": "MyProject",
"environment": "production",
"date": "2025-11-08",
"user": {
"name": "JohnDoe",
"role": "admin"
},
"database": {
"host": "db.example.com",
"port": 5432,
"credentials": {
"username": "dbuser",
"password": "secret"
}
}
}
Running the Example
# If installed as a package
deployfolder example_config.yaml --values example_values.json
# Or using the module directly
python -m deployfolder example_config.yaml --values example_values.json
This will create:
- A folder named "MyProject_deploy"
- Files copied and renamed according to the configuration
- Empty folders as specified
- Generated files from templates (config file, report, HTML document)
- A zip file of the entire folder
Error Handling
The tool provides error messages for common issues:
- Missing or invalid configuration file
- Missing or invalid values file
- File not found errors
- Permission errors
Testing
The project includes comprehensive unit tests to ensure all functionality works correctly. The tests are implemented using Python's built-in unittest framework.
Running the Tests
To run all the unit tests:
# Run all tests
python -m unittest discover -s tests
# Run a specific test file
python -m unittest tests/test_main.py
# Run the nested JSON test script
python -m tests.test_nested_json
Adding New Tests
To add new tests, extend the existing test classes in tests/test_main.py or create new test classes that inherit from unittest.TestCase. Follow the existing patterns for setting up test fixtures and cleaning up after tests.
License
This project is open source and available under the MIT License.
Copyright (c) 2025 Janosch Meyer (janosch.code@proton.me)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Note: This project was created with the assistance of artificial intelligence.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file deployfolder-0.2.0.tar.gz.
File metadata
- Download URL: deployfolder-0.2.0.tar.gz
- Upload date:
- Size: 20.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d8bef9ab0ab62cb8939994d45048c0e7210f30dc2ecb6d4d01d4edc7958ef0c5
|
|
| MD5 |
c71c129c3ccc4dc63e57bb3f2c96c8d6
|
|
| BLAKE2b-256 |
057a62eb50307707ebbcf66b74c8b07a8b1ef72ef35602b48e026b5d2a32eb1b
|
File details
Details for the file deployfolder-0.2.0-py3-none-any.whl.
File metadata
- Download URL: deployfolder-0.2.0-py3-none-any.whl
- Upload date:
- Size: 12.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
990e0f740ff8fd20963606bb424fbfc6285ba542d2b236c5ddd0c8b90c587e72
|
|
| MD5 |
64348606a0e068482d2f1563331aa524
|
|
| BLAKE2b-256 |
f7e76843655213a355959249fc8d092f85eea943550c5008fc8000cd9b7f1eaa
|