mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-05-18 18:50:32 +02:00
Optimized Docker Matrix Role in Preparation for use on CyMaIS.Cloud Server
This commit is contained in:
parent
9c65c320f9
commit
76aef5949b
@ -0,0 +1,2 @@
|
|||||||
|
from pkgutil import extend_path
|
||||||
|
__path__ = extend_path(__path__, __name__)
|
@ -1,3 +1,4 @@
|
|||||||
|
CYMAIS_ENVIRONMENT: "production"
|
||||||
|
|
||||||
HOST_CURRENCY: "EUR"
|
HOST_CURRENCY: "EUR"
|
||||||
HOST_TIMEZONE: "UTC"
|
HOST_TIMEZONE: "UTC"
|
||||||
|
@ -12,7 +12,7 @@ SSH_PORT={{ports.public.ssh[application_id]}}
|
|||||||
SSH_LISTEN_PORT=22
|
SSH_LISTEN_PORT=22
|
||||||
DOMAIN={{domains[application_id]}}
|
DOMAIN={{domains[application_id]}}
|
||||||
SSH_DOMAIN={{domains[application_id]}}
|
SSH_DOMAIN={{domains[application_id]}}
|
||||||
RUN_MODE="{{run_mode}}"
|
RUN_MODE="{{ 'dev' if (CYMAIS_ENVIRONMENT | lower) == 'development' else 'prod' }}"
|
||||||
ROOT_URL="{{ web_protocol }}://{{domains[application_id]}}/"
|
ROOT_URL="{{ web_protocol }}://{{domains[application_id]}}/"
|
||||||
|
|
||||||
# Mail Configuration
|
# Mail Configuration
|
||||||
|
@ -15,3 +15,5 @@ csp:
|
|||||||
flags:
|
flags:
|
||||||
script-src:
|
script-src:
|
||||||
unsafe-inline: true
|
unsafe-inline: true
|
||||||
|
style-src:
|
||||||
|
unsafe-inline: true
|
5
roles/docker-matrix/Todo.md
Normal file
5
roles/docker-matrix/Todo.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Todo
|
||||||
|
- Enable Whatsapp by default
|
||||||
|
- Enable Telegram by default
|
||||||
|
- Enable Slack by default
|
||||||
|
- Enable ChatGPT by default
|
2
roles/docker-matrix/filter_plugins/__init__.py
Normal file
2
roles/docker-matrix/filter_plugins/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from pkgutil import extend_path
|
||||||
|
__path__ = extend_path(__path__, __name__)
|
13
roles/docker-matrix/filter_plugins/bridge_filters.py
Normal file
13
roles/docker-matrix/filter_plugins/bridge_filters.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
def filter_enabled_bridges(bridges, plugins):
|
||||||
|
"""
|
||||||
|
Return only those bridge definitions whose 'bridge_name' is set to True in plugins.
|
||||||
|
:param bridges: list of dicts, each with a 'bridge_name' key
|
||||||
|
:param plugins: dict mapping bridge_name to a boolean
|
||||||
|
"""
|
||||||
|
return [b for b in bridges if plugins.get(b['bridge_name'], False)]
|
||||||
|
|
||||||
|
class FilterModule(object):
|
||||||
|
def filters(self):
|
||||||
|
return {
|
||||||
|
'filter_enabled_bridges': filter_enabled_bridges,
|
||||||
|
}
|
@ -1,4 +1,13 @@
|
|||||||
---
|
---
|
||||||
|
- name: Load bridges configuration
|
||||||
|
include_vars:
|
||||||
|
file: "bridges.yml"
|
||||||
|
|
||||||
|
- name: Filter enabled bridges and register as fact
|
||||||
|
set_fact:
|
||||||
|
bridges: "{{ bridges_configuration | filter_enabled_bridges(applications[application_id].plugins) }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
- name: "include docker-central-database"
|
- name: "include docker-central-database"
|
||||||
include_role:
|
include_role:
|
||||||
name: docker-central-database
|
name: docker-central-database
|
||||||
|
@ -25,7 +25,11 @@ services:
|
|||||||
interval: 1m
|
interval: 1m
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
|
{% if bridges | bool %}
|
||||||
{% include 'templates/docker/container/depends-on-also-database.yml.j2' %}
|
{% include 'templates/docker/container/depends-on-also-database.yml.j2' %}
|
||||||
|
{% else %}
|
||||||
|
{% include 'templates/docker/container/depends-on-just-database.yml.j2' %}
|
||||||
|
{% endif %}
|
||||||
{% for item in bridges %}
|
{% for item in bridges %}
|
||||||
mautrix-{{item.bridge_name}}:
|
mautrix-{{item.bridge_name}}:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@ -61,47 +65,47 @@ services:
|
|||||||
retries: 3
|
retries: 3
|
||||||
{% include 'templates/docker/container/networks.yml.j2' %}
|
{% include 'templates/docker/container/networks.yml.j2' %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
# Deactivated chatgpt.
|
{% if applications[application_id] | bool %}
|
||||||
# @todo needs to be reactivated as soon as bug is found
|
matrix-chatgpt-bot:
|
||||||
# matrix-chatgpt-bot:
|
restart: {{docker_restart_policy}}
|
||||||
# restart: {{docker_restart_policy}}
|
container_name: matrix-chatgpt
|
||||||
# container_name: matrix-chatgpt
|
image: ghcr.io/matrixgpt/matrix-chatgpt-bot:latest
|
||||||
# image: ghcr.io/matrixgpt/matrix-chatgpt-bot:latest
|
volumes:
|
||||||
# volumes:
|
- chatgpt_data:/storage
|
||||||
# - chatgpt_data:/storage
|
environment:
|
||||||
# environment:
|
OPENAI_API_KEY: '{{applications[application_id].credentials.chatgpt_bridge_openai_api_key}}'
|
||||||
# OPENAI_API_KEY: '{{applications[application_id].credentials.chatgpt_bridge_openai_api_key}}'
|
# Uncomment the next two lines if you are using Azure OpenAI API
|
||||||
# # Uncomment the next two lines if you are using Azure OpenAI API
|
# OPENAI_AZURE: 'false'
|
||||||
# # OPENAI_AZURE: 'false'
|
# CHATGPT_REVERSE_PROXY: 'your-completion-endpoint-here'
|
||||||
# # CHATGPT_REVERSE_PROXY: 'your-completion-endpoint-here'
|
CHATGPT_CONTEXT: 'thread'
|
||||||
# CHATGPT_CONTEXT: 'thread'
|
CHATGPT_API_MODEL: 'gpt-3.5-turbo'
|
||||||
# CHATGPT_API_MODEL: 'gpt-3.5-turbo'
|
# Uncomment and edit the next line if needed
|
||||||
# # Uncomment and edit the next line if needed
|
# CHATGPT_PROMPT_PREFIX: 'Instructions:\nYou are ChatGPT, a large language model trained by OpenAI.'
|
||||||
# # CHATGPT_PROMPT_PREFIX: 'Instructions:\nYou are ChatGPT, a large language model trained by OpenAI.'
|
# CHATGPT_IGNORE_MEDIA: 'false'
|
||||||
# # CHATGPT_IGNORE_MEDIA: 'false'
|
CHATGPT_REVERSE_PROXY: 'https://api.openai.com/v1/chat/completions'
|
||||||
# CHATGPT_REVERSE_PROXY: 'https://api.openai.com/v1/chat/completions'
|
# Uncomment and edit the next line if needed
|
||||||
# # Uncomment and edit the next line if needed
|
# CHATGPT_TEMPERATURE: '0.8'
|
||||||
# # CHATGPT_TEMPERATURE: '0.8'
|
# Uncomment and edit the next line if needed
|
||||||
# # Uncomment and edit the next line if needed
|
#CHATGPT_MAX_CONTEXT_TOKENS: '4097'
|
||||||
# #CHATGPT_MAX_CONTEXT_TOKENS: '4097'
|
CHATGPT_MAX_PROMPT_TOKENS: '3000'
|
||||||
# CHATGPT_MAX_PROMPT_TOKENS: '3000'
|
KEYV_BACKEND: 'file'
|
||||||
# KEYV_BACKEND: 'file'
|
KEYV_URL: ''
|
||||||
# KEYV_URL: ''
|
KEYV_BOT_ENCRYPTION: 'false'
|
||||||
# KEYV_BOT_ENCRYPTION: 'false'
|
KEYV_BOT_STORAGE: 'true'
|
||||||
# KEYV_BOT_STORAGE: 'true'
|
MATRIX_HOMESERVER_URL: 'https://{{domains.synapse}}'
|
||||||
# MATRIX_HOMESERVER_URL: 'https://{{domains.synapse}}'
|
MATRIX_BOT_USERNAME: '@chatgptbot:{{applications.matrix.server_name}}'
|
||||||
# MATRIX_BOT_USERNAME: '@chatgptbot:{{applications.matrix.server_name}}'
|
MATRIX_ACCESS_TOKEN: '{{ applications[application_id].credentials.chatgpt_bridge_access_token | default('') }}'
|
||||||
# MATRIX_ACCESS_TOKEN: '{{ applications[application_id].credentials.chatgpt_bridge_access_token | default('') }}'
|
MATRIX_BOT_PASSWORD: '{{applications[application_id].credentials.chatgpt_bridge_user_password}}'
|
||||||
# MATRIX_BOT_PASSWORD: '{{applications[application_id].credentials.chatgpt_bridge_user_password}}'
|
MATRIX_DEFAULT_PREFIX: '!chatgpt'
|
||||||
# MATRIX_DEFAULT_PREFIX: '!chatgpt'
|
MATRIX_DEFAULT_PREFIX_REPLY: 'false'
|
||||||
# MATRIX_DEFAULT_PREFIX_REPLY: 'false'
|
#MATRIX_BLACKLIST: ''
|
||||||
# #MATRIX_BLACKLIST: ''
|
MATRIX_WHITELIST: ':{{applications.matrix.server_name}}'
|
||||||
# MATRIX_WHITELIST: ':{{applications.matrix.server_name}}'
|
MATRIX_AUTOJOIN: 'true'
|
||||||
# MATRIX_AUTOJOIN: 'true'
|
MATRIX_ENCRYPTION: 'true'
|
||||||
# MATRIX_ENCRYPTION: 'true'
|
MATRIX_THREADS: 'true'
|
||||||
# MATRIX_THREADS: 'true'
|
MATRIX_PREFIX_DM: 'false'
|
||||||
# MATRIX_PREFIX_DM: 'false'
|
MATRIX_RICH_TEXT: 'true'
|
||||||
# MATRIX_RICH_TEXT: 'true'
|
{% endif %}
|
||||||
|
|
||||||
{% include 'templates/docker/compose/volumes.yml.j2' %}
|
{% include 'templates/docker/compose/volumes.yml.j2' %}
|
||||||
synapse_data:
|
synapse_data:
|
||||||
|
@ -61,7 +61,9 @@ oidc_providers:
|
|||||||
backchannel_logout_enabled: true
|
backchannel_logout_enabled: true
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if bridges | bool %}
|
||||||
app_service_config_files:
|
app_service_config_files:
|
||||||
{% for item in bridges %}
|
{% for item in bridges %}
|
||||||
- {{registration_file_folder}}{{item.bridge_name}}.registration.yaml
|
- {{registration_file_folder}}{{item.bridge_name}}.registration.yaml
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endif %}
|
30
roles/docker-matrix/vars/bridges.yml
Normal file
30
roles/docker-matrix/vars/bridges.yml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
bridges_configuration:
|
||||||
|
- database_password: "{{ applications[application_id].credentials.mautrix_whatsapp_bridge_database_password }}"
|
||||||
|
database_username: "mautrix_whatsapp_bridge"
|
||||||
|
database_name: "mautrix_whatsapp_bridge"
|
||||||
|
bridge_name: "whatsapp"
|
||||||
|
|
||||||
|
- database_password: "{{ applications[application_id].credentials.mautrix_telegram_bridge_database_password }}"
|
||||||
|
database_username: "mautrix_telegram_bridge"
|
||||||
|
database_name: "mautrix_telegram_bridge"
|
||||||
|
bridge_name: "telegram"
|
||||||
|
|
||||||
|
- database_password: "{{ applications[application_id].credentials.mautrix_signal_bridge_database_password }}"
|
||||||
|
database_username: "mautrix_signal_bridge"
|
||||||
|
database_name: "mautrix_signal_bridge"
|
||||||
|
bridge_name: "signal"
|
||||||
|
|
||||||
|
- database_password: "{{ applications[application_id].credentials.mautrix_slack_bridge_database_password }}"
|
||||||
|
database_username: "mautrix_slack_bridge"
|
||||||
|
database_name: "mautrix_slack_bridge"
|
||||||
|
bridge_name: "slack"
|
||||||
|
|
||||||
|
- database_password: "{{ applications[application_id].credentials.mautrix_facebook_bridge_database_password }}"
|
||||||
|
database_username: "mautrix_facebook_bridge"
|
||||||
|
database_name: "mautrix_facebook_bridge"
|
||||||
|
bridge_name: "facebook"
|
||||||
|
|
||||||
|
- database_password: "{{ applications[application_id].credentials.mautrix_instagram_bridge_database_password }}"
|
||||||
|
database_username: "mautrix_instagram_bridge"
|
||||||
|
database_name: "mautrix_instagram_bridge"
|
||||||
|
bridge_name: "instagram"
|
@ -3,7 +3,6 @@ users:
|
|||||||
administrator:
|
administrator:
|
||||||
username: "{{users.administrator.username}}" # Accountname of the matrix admin
|
username: "{{users.administrator.username}}" # Accountname of the matrix admin
|
||||||
playbook_tags: "setup-all,start" # For the initial update use: install-all,ensure-matrix-users-created,start
|
playbook_tags: "setup-all,start" # For the initial update use: install-all,ensure-matrix-users-created,start
|
||||||
role: "compose" # Role to setup Matrix. Valid values: ansible, compose
|
|
||||||
server_name: "{{primary_domain}}" # Adress for the account names etc.
|
server_name: "{{primary_domain}}" # Adress for the account names etc.
|
||||||
synapse:
|
synapse:
|
||||||
version: "latest"
|
version: "latest"
|
||||||
@ -30,3 +29,14 @@ csp:
|
|||||||
script-src:
|
script-src:
|
||||||
- "{{ domains.synapse }}"
|
- "{{ domains.synapse }}"
|
||||||
- "https://cdn.jsdelivr.net"
|
- "https://cdn.jsdelivr.net"
|
||||||
|
plugins:
|
||||||
|
# You need to enable them in the inventory file
|
||||||
|
chatgpt: false
|
||||||
|
facebook: false
|
||||||
|
immesage: false
|
||||||
|
instagram: false
|
||||||
|
signal: false
|
||||||
|
slack: false
|
||||||
|
telegram: false
|
||||||
|
whatsapp: false
|
||||||
|
|
||||||
|
@ -3,36 +3,3 @@ application_id: "matrix"
|
|||||||
database_type: "postgres"
|
database_type: "postgres"
|
||||||
registration_file_folder: "/data/"
|
registration_file_folder: "/data/"
|
||||||
well_known_directory: "{{nginx.directories.data.well_known}}/matrix/"
|
well_known_directory: "{{nginx.directories.data.well_known}}/matrix/"
|
||||||
|
|
||||||
bridges:
|
|
||||||
- database_password: "{{ applications[application_id].credentials.mautrix_whatsapp_bridge_database_password }}"
|
|
||||||
database_username: "mautrix_whatsapp_bridge"
|
|
||||||
database_name: "mautrix_whatsapp_bridge"
|
|
||||||
bridge_name: "whatsapp"
|
|
||||||
|
|
||||||
- database_password: "{{ applications[application_id].credentials.mautrix_telegram_bridge_database_password }}"
|
|
||||||
database_username: "mautrix_telegram_bridge"
|
|
||||||
database_name: "mautrix_telegram_bridge"
|
|
||||||
bridge_name: "telegram"
|
|
||||||
|
|
||||||
- database_password: "{{ applications[application_id].credentials.mautrix_signal_bridge_database_password }}"
|
|
||||||
database_username: "mautrix_signal_bridge"
|
|
||||||
database_name: "mautrix_signal_bridge"
|
|
||||||
bridge_name: "signal"
|
|
||||||
|
|
||||||
# Deactivated temporary, due to bug which is hard to find
|
|
||||||
# @todo Reactivate
|
|
||||||
# - database_password: "{{ applications[application_id].credentials.mautrix_slack_bridge_database_password }}"
|
|
||||||
# database_username: "mautrix_slack_bridge"
|
|
||||||
# database_name: "mautrix_slack_bridge"
|
|
||||||
# bridge_name: "slack"
|
|
||||||
|
|
||||||
- database_password: "{{ applications[application_id].credentials.mautrix_facebook_bridge_database_password }}"
|
|
||||||
database_username: "mautrix_facebook_bridge"
|
|
||||||
database_name: "mautrix_facebook_bridge"
|
|
||||||
bridge_name: "facebook"
|
|
||||||
|
|
||||||
- database_password: "{{ applications[application_id].credentials.mautrix_instagram_bridge_database_password }}"
|
|
||||||
database_username: "mautrix_instagram_bridge"
|
|
||||||
database_name: "mautrix_instagram_bridge"
|
|
||||||
bridge_name: "instagram"
|
|
@ -96,7 +96,7 @@
|
|||||||
shell: >
|
shell: >
|
||||||
docker compose exec web bash -c "
|
docker compose exec web bash -c "
|
||||||
cd /app &&
|
cd /app &&
|
||||||
RAILS_ENV=production bundle exec rails runner \"
|
RAILS_ENV={{ CYMAIS_ENVIRONMENT | lower }} bundle exec rails runner \"
|
||||||
user = User.find_by(mail: '{{ users.administrator.email }}');
|
user = User.find_by(mail: '{{ users.administrator.email }}');
|
||||||
if user.nil?;
|
if user.nil?;
|
||||||
puts 'User with email {{ users.administrator.email }} not found.';
|
puts 'User with email {{ users.administrator.email }} not found.';
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
- name: Set settings in OpenProject
|
- name: Set settings in OpenProject
|
||||||
shell: >
|
shell: >
|
||||||
docker compose exec web bash -c "cd /app &&
|
docker compose exec web bash -c "cd /app &&
|
||||||
RAILS_ENV=production bundle exec rails runner \"Setting[:{{ item.key }}] = '{{ item.value }}'\""
|
RAILS_ENV={{ CYMAIS_ENVIRONMENT | lower }} bundle exec rails runner \"Setting[:{{ item.key }}] = '{{ item.value }}'\""
|
||||||
args:
|
args:
|
||||||
chdir: "{{ docker_compose.directories.instance }}"
|
chdir: "{{ docker_compose.directories.instance }}"
|
||||||
loop: "{{ openproject_rails_settings | dict2items }}"
|
loop: "{{ openproject_rails_settings | dict2items }}"
|
||||||
|
@ -149,7 +149,7 @@ def update_mastodon():
|
|||||||
Runs the database migration for Mastodon to ensure all required tables are up to date.
|
Runs the database migration for Mastodon to ensure all required tables are up to date.
|
||||||
"""
|
"""
|
||||||
print("Starting Mastodon database migration.")
|
print("Starting Mastodon database migration.")
|
||||||
run_command("docker compose exec -T web bash -c 'RAILS_ENV=production bin/rails db:migrate'")
|
run_command("docker compose exec -T web bash -c 'RAILS_ENV={{ CYMAIS_ENVIRONMENT | lower }} bin/rails db:migrate'")
|
||||||
print("Mastodon database migration complete.")
|
print("Mastodon database migration complete.")
|
||||||
|
|
||||||
def upgrade_listmonk():
|
def upgrade_listmonk():
|
||||||
|
64
tests/unit/test_bridge_filters.py
Normal file
64
tests/unit/test_bridge_filters.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
# Add the filter_plugins directory from the docker-matrix role to the import path
|
||||||
|
sys.path.insert(
|
||||||
|
0,
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), "../../roles/docker-matrix")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
from filter_plugins.bridge_filters import filter_enabled_bridges
|
||||||
|
|
||||||
|
|
||||||
|
class TestBridgeFilters(unittest.TestCase):
|
||||||
|
def test_no_bridges_returns_empty_list(self):
|
||||||
|
# When there are no bridges defined, result should be an empty list
|
||||||
|
self.assertEqual(filter_enabled_bridges([], {}), [])
|
||||||
|
|
||||||
|
def test_all_bridges_disabled(self):
|
||||||
|
# Define two bridges, but plugins dict has them disabled or missing
|
||||||
|
bridges = [
|
||||||
|
{'bridge_name': 'whatsapp', 'config': {}},
|
||||||
|
{'bridge_name': 'telegram', 'config': {}},
|
||||||
|
]
|
||||||
|
plugins = {
|
||||||
|
'whatsapp': False,
|
||||||
|
'telegram': False,
|
||||||
|
}
|
||||||
|
result = filter_enabled_bridges(bridges, plugins)
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
|
def test_some_bridges_enabled(self):
|
||||||
|
# Only bridges with True in plugins should be returned
|
||||||
|
bridges = [
|
||||||
|
{'bridge_name': 'whatsapp', 'version': '1.0'},
|
||||||
|
{'bridge_name': 'telegram', 'version': '1.0'},
|
||||||
|
{'bridge_name': 'signal', 'version': '1.0'},
|
||||||
|
]
|
||||||
|
plugins = {
|
||||||
|
'whatsapp': True,
|
||||||
|
'telegram': False,
|
||||||
|
'signal': True,
|
||||||
|
}
|
||||||
|
result = filter_enabled_bridges(bridges, plugins)
|
||||||
|
expected = [
|
||||||
|
{'bridge_name': 'whatsapp', 'version': '1.0'},
|
||||||
|
{'bridge_name': 'signal', 'version': '1.0'},
|
||||||
|
]
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_bridge_without_plugin_entry_defaults_to_disabled(self):
|
||||||
|
# If a bridge_name is not present in plugins, it should be treated as disabled
|
||||||
|
bridges = [
|
||||||
|
{'bridge_name': 'facebook', 'enabled': True},
|
||||||
|
]
|
||||||
|
plugins = {} # no entries
|
||||||
|
result = filter_enabled_bridges(bridges, plugins)
|
||||||
|
self.assertEqual(result, [])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
@ -1,6 +1,16 @@
|
|||||||
# tests/unit/test_configuration_filters.py
|
# tests/unit/test_configuration_filters.py
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0,
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), "../../")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
from filter_plugins.configuration_filters import (
|
from filter_plugins.configuration_filters import (
|
||||||
is_feature_enabled,
|
is_feature_enabled,
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import hashlib
|
import hashlib
|
||||||
import base64
|
import base64
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.insert(
|
||||||
|
0,
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), "../../")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
from filter_plugins.csp_filters import FilterModule, AnsibleFilterError
|
from filter_plugins.csp_filters import FilterModule, AnsibleFilterError
|
||||||
|
|
||||||
class TestCspFilters(unittest.TestCase):
|
class TestCspFilters(unittest.TestCase):
|
||||||
|
@ -2,8 +2,12 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
|
sys.path.insert(
|
||||||
sys.path.insert(0, PROJECT_ROOT)
|
0,
|
||||||
|
os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), "../../")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
from filter_plugins.redirect_filters import FilterModule
|
from filter_plugins.redirect_filters import FilterModule
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user