mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-01-03 05:40:23 +00:00
Compare commits
21 Commits
docker-bui
...
parallel-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca63dad896 | ||
|
|
bd9b72dbfa | ||
|
|
8473da4bdb | ||
|
|
762e2dacb2 | ||
|
|
62ea1f9b24 | ||
|
|
14a6ced8f4 | ||
|
|
465e5e2ecc | ||
|
|
ada63a3200 | ||
|
|
eef5425908 | ||
|
|
096bd21663 | ||
|
|
0f53233272 | ||
|
|
faaa9937d6 | ||
|
|
950d59ccfa | ||
|
|
bd3f0360e4 | ||
|
|
57347fd55c | ||
|
|
8ef782760a | ||
|
|
4e20fce82c | ||
|
|
7d8c127e1f | ||
|
|
0ca2acd38c | ||
|
|
2a0131d0f4 | ||
|
|
9ed236434e |
2
.github/workflows/test-container-build.yml
vendored
2
.github/workflows/test-container-build.yml
vendored
@@ -82,5 +82,5 @@ jobs:
|
|||||||
file: ${{ matrix.dockerfile }}
|
file: ${{ matrix.dockerfile }}
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=min
|
||||||
|
|
||||||
|
|||||||
7
.github/workflows/test-only.yml
vendored
7
.github/workflows/test-only.yml
vendored
@@ -21,8 +21,6 @@ jobs:
|
|||||||
python3 -c "from openapi_spec_validator import validate_spec; import yaml; validate_spec(yaml.safe_load(open('docs/api-spec.yaml')))"
|
python3 -c "from openapi_spec_validator import validate_spec; import yaml; validate_spec(yaml.safe_load(open('docs/api-spec.yaml')))"
|
||||||
|
|
||||||
test-application-3-10:
|
test-application-3-10:
|
||||||
# Only run on push to master (including PR merges)
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
||||||
needs: lint-code
|
needs: lint-code
|
||||||
uses: ./.github/workflows/test-stack-reusable-workflow.yml
|
uses: ./.github/workflows/test-stack-reusable-workflow.yml
|
||||||
with:
|
with:
|
||||||
@@ -30,15 +28,12 @@ jobs:
|
|||||||
|
|
||||||
|
|
||||||
test-application-3-11:
|
test-application-3-11:
|
||||||
# Always run
|
|
||||||
needs: lint-code
|
needs: lint-code
|
||||||
uses: ./.github/workflows/test-stack-reusable-workflow.yml
|
uses: ./.github/workflows/test-stack-reusable-workflow.yml
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
|
|
||||||
test-application-3-12:
|
test-application-3-12:
|
||||||
# Only run on push to master (including PR merges)
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
||||||
needs: lint-code
|
needs: lint-code
|
||||||
uses: ./.github/workflows/test-stack-reusable-workflow.yml
|
uses: ./.github/workflows/test-stack-reusable-workflow.yml
|
||||||
with:
|
with:
|
||||||
@@ -46,8 +41,6 @@ jobs:
|
|||||||
skip-pypuppeteer: true
|
skip-pypuppeteer: true
|
||||||
|
|
||||||
test-application-3-13:
|
test-application-3-13:
|
||||||
# Only run on push to master (including PR merges)
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
||||||
needs: lint-code
|
needs: lint-code
|
||||||
uses: ./.github/workflows/test-stack-reusable-workflow.yml
|
uses: ./.github/workflows/test-stack-reusable-workflow.yml
|
||||||
with:
|
with:
|
||||||
|
|||||||
32
Dockerfile
32
Dockerfile
@@ -34,27 +34,25 @@ ENV OPENSSL_LIB_DIR="/usr/lib/arm-linux-gnueabihf"
|
|||||||
ENV OPENSSL_INCLUDE_DIR="/usr/include/openssl"
|
ENV OPENSSL_INCLUDE_DIR="/usr/include/openssl"
|
||||||
# Additional environment variables for cryptography Rust build
|
# Additional environment variables for cryptography Rust build
|
||||||
ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1
|
ENV CRYPTOGRAPHY_DONT_BUILD_RUST=1
|
||||||
RUN --mount=type=cache,id=pip,sharing=locked,target=/tmp/pip-cache \
|
RUN --mount=type=cache,target=/tmp/pip-cache \
|
||||||
pip install \
|
pip install \
|
||||||
--prefer-binary \
|
--prefer-binary \
|
||||||
--extra-index-url https://www.piwheels.org/simple \
|
--extra-index-url https://www.piwheels.org/simple \
|
||||||
--extra-index-url https://pypi.anaconda.org/ARM-software/simple \
|
--extra-index-url https://pypi.anaconda.org/ARM-software/simple \
|
||||||
--cache-dir=/tmp/pip-cache \
|
--cache-dir=/tmp/pip-cache \
|
||||||
--target=/dependencies \
|
--target=/dependencies \
|
||||||
-r /requirements.txt
|
-r /requirements.txt
|
||||||
|
|
||||||
|
|
||||||
# Playwright is an alternative to Selenium
|
# Playwright is an alternative to Selenium
|
||||||
# Excluded this package from requirements.txt to prevent arm/v6 and arm/v7 builds from failing
|
# Excluded this package from requirements.txt to prevent arm/v6 and arm/v7 builds from failing
|
||||||
# https://github.com/dgtlmoon/changedetection.io/pull/1067 also musl/alpine (not supported)
|
# https://github.com/dgtlmoon/changedetection.io/pull/1067 also musl/alpine (not supported)
|
||||||
RUN --mount=type=cache,id=pip,sharing=locked,target=/tmp/pip-cache \
|
RUN --mount=type=cache,target=/tmp/pip-cache \
|
||||||
pip install \
|
pip install \
|
||||||
--prefer-binary \
|
--prefer-binary \
|
||||||
--cache-dir=/tmp/pip-cache \
|
--cache-dir=/tmp/pip-cache \
|
||||||
--target=/dependencies \
|
--target=/dependencies \
|
||||||
playwright~=1.48.0 \
|
playwright~=1.48.0 \
|
||||||
|| echo "WARN: Failed to install Playwright. The application can still run, but the Playwright option will be disabled."
|
|| echo "WARN: Failed to install Playwright. The application can still run, but the Playwright option will be disabled."
|
||||||
|
|
||||||
|
|
||||||
# Final image stage
|
# Final image stage
|
||||||
FROM python:${PYTHON_VERSION}-slim-bookworm
|
FROM python:${PYTHON_VERSION}-slim-bookworm
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Read more https://github.com/dgtlmoon/changedetection.io/wiki
|
# Read more https://github.com/dgtlmoon/changedetection.io/wiki
|
||||||
|
|
||||||
__version__ = '0.50.35'
|
__version__ = '0.50.33'
|
||||||
|
|
||||||
from changedetectionio.strtobool import strtobool
|
from changedetectionio.strtobool import strtobool
|
||||||
from json.decoder import JSONDecodeError
|
from json.decoder import JSONDecodeError
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
|||||||
return make_response("Error: You must have atleast one watch configured for 'test notification' to work", 400)
|
return make_response("Error: You must have atleast one watch configured for 'test notification' to work", 400)
|
||||||
|
|
||||||
watch = datastore.data['watching'].get(watch_uuid)
|
watch = datastore.data['watching'].get(watch_uuid)
|
||||||
notification_urls = request.form.get('notification_urls','').strip().splitlines()
|
|
||||||
|
notification_urls = None
|
||||||
|
|
||||||
|
if request.form.get('notification_urls'):
|
||||||
|
notification_urls = request.form['notification_urls'].strip().splitlines()
|
||||||
|
|
||||||
if not notification_urls:
|
if not notification_urls:
|
||||||
logger.debug("Test notification - Trying by group/tag in the edit form if available")
|
logger.debug("Test notification - Trying by group/tag in the edit form if available")
|
||||||
@@ -77,8 +81,6 @@ def construct_blueprint(datastore: ChangeDetectionStore):
|
|||||||
# Only use if present, if not set in n_object it should use the default system value
|
# Only use if present, if not set in n_object it should use the default system value
|
||||||
if 'notification_format' in request.form and request.form['notification_format'].strip():
|
if 'notification_format' in request.form and request.form['notification_format'].strip():
|
||||||
n_object['notification_format'] = request.form.get('notification_format', '').strip()
|
n_object['notification_format'] = request.form.get('notification_format', '').strip()
|
||||||
else:
|
|
||||||
n_object['notification_format'] = datastore.data['settings']['application'].get('notification_format')
|
|
||||||
|
|
||||||
if 'notification_title' in request.form and request.form['notification_title'].strip():
|
if 'notification_title' in request.form and request.form['notification_title'].strip():
|
||||||
n_object['notification_title'] = request.form.get('notification_title', '').strip()
|
n_object['notification_title'] = request.form.get('notification_title', '').strip()
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def as_monospaced_html_email(content: str, title: str) -> str:
|
|||||||
</head>
|
</head>
|
||||||
<body style="-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
|
<body style="-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;">
|
||||||
<pre role="article" aria-roledescription="email" lang="en"
|
<pre role="article" aria-roledescription="email" lang="en"
|
||||||
style="font-family: monospace, 'Courier New', Courier; font-size: 0.9rem;
|
style="font-family: monospace, 'Courier New', Courier; font-size: 0.8em;
|
||||||
white-space: pre-wrap; word-break: break-word;">{content}</pre>
|
white-space: pre-wrap; word-break: break-word;">{content}</pre>
|
||||||
</body>
|
</body>
|
||||||
</html>"""
|
</html>"""
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ $(document).ready(function () {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
notification_urls: $('textarea.notification-urls').val(),
|
notification_body: $('#notification_body').val(),
|
||||||
notification_title: $('input.notification-title').val(),
|
notification_format: $('#notification_format').val(),
|
||||||
notification_body: $('textarea.notification-body').val(),
|
notification_title: $('#notification_title').val(),
|
||||||
notification_format: $('select.notification-format').val(),
|
notification_urls: $('.notification-urls').val(),
|
||||||
tags: $('#tags').val(),
|
tags: $('#tags').val(),
|
||||||
window_url: window.location.href,
|
window_url: window.location.href,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,8 +261,7 @@ class ChangeDetectionStore:
|
|||||||
self.__data['watching'] = {}
|
self.__data['watching'] = {}
|
||||||
time.sleep(1) # Mainly used for testing to allow all items to flush before running next test
|
time.sleep(1) # Mainly used for testing to allow all items to flush before running next test
|
||||||
for uuid in self.data['watching']:
|
for uuid in self.data['watching']:
|
||||||
path = pathlib.Path(
|
path = pathlib.Path(os.path.join(self.datastore_path, uuid))
|
||||||
os.path.join(self.datastore_path, uuid))
|
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
self.delete(uuid)
|
self.delete(uuid)
|
||||||
|
|
||||||
@@ -1006,38 +1005,28 @@ class ChangeDetectionStore:
|
|||||||
|
|
||||||
|
|
||||||
# Some notification formats got the wrong name type
|
# Some notification formats got the wrong name type
|
||||||
def update_23(self):
|
def update_22(self):
|
||||||
|
|
||||||
def re_run(formats):
|
|
||||||
sys_n_format = self.data['settings']['application'].get('notification_format')
|
|
||||||
key_exists_as_value = next((k for k, v in formats.items() if v == sys_n_format), None)
|
|
||||||
if key_exists_as_value: # key of "Plain text"
|
|
||||||
logger.success(f"['settings']['application']['notification_format'] '{sys_n_format}' -> '{key_exists_as_value}'")
|
|
||||||
self.data['settings']['application']['notification_format'] = key_exists_as_value
|
|
||||||
|
|
||||||
for uuid, watch in self.data['watching'].items():
|
|
||||||
n_format = self.data['watching'][uuid].get('notification_format')
|
|
||||||
key_exists_as_value = next((k for k, v in formats.items() if v == n_format), None)
|
|
||||||
if key_exists_as_value and key_exists_as_value != USE_SYSTEM_DEFAULT_NOTIFICATION_FORMAT_FOR_WATCH: # key of "Plain text"
|
|
||||||
logger.success(f"['watching'][{uuid}]['notification_format'] '{n_format}' -> '{key_exists_as_value}'")
|
|
||||||
self.data['watching'][uuid]['notification_format'] = key_exists_as_value # should be 'text' or whatever
|
|
||||||
|
|
||||||
for uuid, tag in self.data['settings']['application']['tags'].items():
|
|
||||||
n_format = self.data['settings']['application']['tags'][uuid].get('notification_format')
|
|
||||||
key_exists_as_value = next((k for k, v in formats.items() if v == n_format), None)
|
|
||||||
if key_exists_as_value and key_exists_as_value != USE_SYSTEM_DEFAULT_NOTIFICATION_FORMAT_FOR_WATCH: # key of "Plain text"
|
|
||||||
logger.success(
|
|
||||||
f"['settings']['application']['tags'][{uuid}]['notification_format'] '{n_format}' -> '{key_exists_as_value}'")
|
|
||||||
self.data['settings']['application']['tags'][uuid][
|
|
||||||
'notification_format'] = key_exists_as_value # should be 'text' or whatever
|
|
||||||
|
|
||||||
from .notification import valid_notification_formats
|
from .notification import valid_notification_formats
|
||||||
formats = deepcopy(valid_notification_formats)
|
|
||||||
re_run(formats)
|
sys_n_format = self.data['settings']['application'].get('notification_format')
|
||||||
# And in previous versions, it was "text" instead of Plain text, Markdown instead of "Markdown to HTML"
|
key_exists_as_value = next((k for k, v in valid_notification_formats.items() if v == sys_n_format), None)
|
||||||
formats['text'] = 'Text'
|
if key_exists_as_value: # key of "Plain text"
|
||||||
formats['markdown'] = 'Markdown'
|
logger.success(f"['settings']['application']['notification_format'] '{sys_n_format}' -> '{key_exists_as_value}'")
|
||||||
re_run(formats)
|
self.data['settings']['application']['notification_format'] = key_exists_as_value
|
||||||
|
|
||||||
|
for uuid, watch in self.data['watching'].items():
|
||||||
|
n_format = self.data['watching'][uuid].get('notification_format')
|
||||||
|
key_exists_as_value = next((k for k, v in valid_notification_formats.items() if v == n_format), None)
|
||||||
|
if key_exists_as_value and key_exists_as_value != USE_SYSTEM_DEFAULT_NOTIFICATION_FORMAT_FOR_WATCH: # key of "Plain text"
|
||||||
|
logger.success(f"['watching'][{uuid}]['notification_format'] '{n_format}' -> '{key_exists_as_value}'")
|
||||||
|
self.data['watching'][uuid]['notification_format'] = key_exists_as_value # should be 'text' or whatever
|
||||||
|
|
||||||
|
for uuid, tag in self.data['settings']['application']['tags'].items():
|
||||||
|
n_format = self.data['settings']['application']['tags'][uuid].get('notification_format')
|
||||||
|
key_exists_as_value = next((k for k, v in valid_notification_formats.items() if v == n_format), None)
|
||||||
|
if key_exists_as_value and key_exists_as_value != USE_SYSTEM_DEFAULT_NOTIFICATION_FORMAT_FOR_WATCH: # key of "Plain text"
|
||||||
|
logger.success(f"['settings']['application']['tags'][{uuid}]['notification_format'] '{n_format}' -> '{key_exists_as_value}'")
|
||||||
|
self.data['settings']['application']['tags'][uuid]['notification_format'] = key_exists_as_value # should be 'text' or whatever
|
||||||
|
|
||||||
def add_notification_url(self, notification_url):
|
def add_notification_url(self, notification_url):
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user