Compare commits

...

19 Commits

Author SHA1 Message Date
cc640700a4 fix: 🐛 Make Commit Author envs optional
All checks were successful
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
In some pipelines the commit author is not available (i.e. cron). Use "Woodpecker" and Woodpecker's Favicon as a placeholder if the author is missing
2025-04-18 11:42:21 +02:00
a84fc769ac Update&fix notifier for new woodpecker version
All checks were successful
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
2025-03-06 09:01:08 +01:00
2b00dcc305 ci: 💚 use jq -r to strip quotes in output
All checks were successful
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
2024-10-31 11:05:24 +01:00
cc526eb592 me be dum
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-10-31 10:56:57 +01:00
b9616bb19e i guess we rawdogging digests over here
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-10-31 10:49:23 +01:00
63e72559be i guess we rawdogging digests over here
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-10-31 10:44:44 +01:00
0b543c5b70 ci: 🚧 pre-log in and pull image with docker for scan?
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-10-31 10:17:46 +01:00
4de3431e50 Make image scan with debug
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-10-31 10:06:56 +01:00
1c9b946e49 fix: 🚑 fix concat issue
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-10-31 09:52:47 +01:00
373532d739 refactor: 🎨 case matching in dockerfile 2024-10-31 09:32:58 +01:00
dc978900b8 ci: 💚 use alternate trivy db repos
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-10-31 09:26:28 +01:00
08947259ae fix(api): 🐛 Fix missing newlines in failed step logfiles
Some checks failed
ci/woodpecker/push/scans Pipeline failed
ci/woodpecker/push/build unknown status
2024-10-31 09:24:46 +01:00
766dc118f9 Update .woodpecker/scans.yml
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-09-12 16:06:40 +02:00
b2f68ef9b8 Update .woodpecker/build.yml
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-09-12 14:24:46 +02:00
e39bae3131 Update .woodpecker/build.yml
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
2024-09-12 13:43:21 +02:00
86e21ce2d3 chore: 🔒 Update dependencies (fix published CVEs)
Some checks failed
ci/woodpecker/push/scans Pipeline was successful
ci/woodpecker/push/build Pipeline failed
none
2024-09-12 11:29:33 +02:00
33b019f694 Update .woodpecker/build.yml
Some checks failed
ci/woodpecker/push/scans Pipeline failed
ci/woodpecker/push/build unknown status
2024-09-12 11:17:37 +02:00
d632af5c8a Update .woodpecker/build.yml
Some checks failed
ci/woodpecker/push/scans Pipeline failed
ci/woodpecker/push/build unknown status
2024-09-12 11:13:44 +02:00
f06500dd17 Update .woodpecker/scans.yml 2024-09-12 11:12:32 +02:00
5 changed files with 98 additions and 72 deletions

View File

@@ -1,6 +1,6 @@
variables: variables:
- &file Containerfile - &file Containerfile
- &repo dev.shielddagger.com/infra/discord-notifier - &repo dev.shielddagger.com/opensource/discord-notifier
when: when:
- event: [push, pull_request] - event: [push, pull_request]
@@ -15,8 +15,6 @@ steps:
- echo ${CI_COMMIT_SHA:0:8} > .version - echo ${CI_COMMIT_SHA:0:8} > .version
- name: dryrun - name: dryrun
image: woodpeckerci/plugin-docker-buildx image: woodpeckerci/plugin-docker-buildx
environment:
IMAGE_REPO_URL: *repo
backend_options: backend_options:
kubernetes: kubernetes:
securityContext: securityContext:
@@ -24,7 +22,7 @@ steps:
settings: settings:
dockerfile: *file dockerfile: *file
platforms: linux/arm64,linux/amd64 platforms: linux/arm64,linux/amd64
cache_from: type=registry,ref=${IMAGE_REPO_URL} cache_from: type=registry,ref=dev.shielddagger.com/opensource/discord-notifier
cache_to: type=inline cache_to: type=inline
dry_run: true dry_run: true
repo: *repo repo: *repo
@@ -45,7 +43,7 @@ steps:
settings: settings:
dockerfile: *file dockerfile: *file
platforms: linux/arm64,linux/amd64 platforms: linux/arm64,linux/amd64
cache_from: type=registry,ref=dev.shielddagger.com/infra/discord-notifier cache_from: type=registry,ref=dev.shielddagger.com/opensource/discord-notifier
cache_to: type=inline cache_to: type=inline
repo: *repo repo: *repo
auto_tag: true auto_tag: true
@@ -58,6 +56,21 @@ steps:
when: when:
- event: push - event: push
branch: main branch: main
- name: gather-digests
image: quay.io/skopeo/stable:latest
environment:
DOCKER_USER:
from_secret: registry_username
DOCKER_PASS:
from_secret: registry_password
when:
- event: push
branch: main
commands:
- dnf install -y jq
- skopeo login dev.shielddagger.com --username $DOCKER_USER --password $DOCKER_PASS
- skopeo inspect --raw docker://dev.shielddagger.com/opensource/discord-notifier:latest | jq -r .'manifests[] | select(.platform.architecture=="arm64").digest' > digest-arm64
- skopeo inspect --raw docker://dev.shielddagger.com/opensource/discord-notifier:latest | jq -r .'manifests[] | select(.platform.architecture=="amd64").digest' > digest-amd64
- name: image-scan - name: image-scan
image: aquasec/trivy image: aquasec/trivy
environment: environment:
@@ -65,13 +78,17 @@ steps:
from_secret: registry_username from_secret: registry_username
TRIVY_PASSWORD: TRIVY_PASSWORD:
from_secret: registry_password from_secret: registry_password
TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db
TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db
TRIVY_CHECKS_BUNDLE_REPOSITORY: public.ecr.aws/aquasecurity/trivy-checks
commands: commands:
- trivy image dev.shielddagger.com/infra/discord-notifier --exit-code 1 --username $TRIVY_USER --severity HIGH,CRITICAL - export ARM64_DIGEST=$(cat digest-arm64)
- trivy image --platform linux/arm64 --debug dev.shielddagger.com/opensource/discord-notifier@$ARM64_DIGEST --exit-code 1 --username $TRIVY_USER --severity HIGH,CRITICAL
when: when:
- event: push - event: push
branch: main branch: main
- name: notify - name: notify
image: dev.shielddagger.com/infra/discord-notifier image: dev.shielddagger.com/opensource/discord-notifier
failure: ignore failure: ignore
settings: settings:
webhook_url: webhook_url:
@@ -79,7 +96,7 @@ steps:
woodpecker_url: https://ci.shielddagger.com/api woodpecker_url: https://ci.shielddagger.com/api
woodpecker_token: woodpecker_token:
from_secret: woodpecker_token from_secret: woodpecker_token
icon_url: https://dev.shielddagger.com/repo-avatars/273e88fa2afde290121dc7b5987dc366b88325f147bf1e5766bca26296bbc1f9 icon_url: https://discord.com/api/webhooks/1231848304694919270/1ApQzOPMfNosxhQ62HbYScBT5s94m0bIUn1IFGQlT6d8Ru2ImcHHjjkFA_SaonBNU3yz
when: when:
- status: [success, failure] - status: [success, failure]

View File

@@ -5,6 +5,10 @@ steps:
- name: scan-repo - name: scan-repo
depends_on: [] depends_on: []
image: aquasec/trivy image: aquasec/trivy
environment:
TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db
TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db
TRIVY_CHECKS_BUNDLE_REPOSITORY: public.ecr.aws/aquasecurity/trivy-checks
commands: commands:
- trivy repo . --exit-code 1 - trivy repo . --exit-code 1
when: when:
@@ -12,12 +16,16 @@ steps:
- name: scan-conf - name: scan-conf
depends_on: [] depends_on: []
image: aquasec/trivy image: aquasec/trivy
environment:
TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db
TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db
TRIVY_CHECKS_BUNDLE_REPOSITORY: public.ecr.aws/aquasecurity/trivy-checks
commands: commands:
- trivy config . --exit-code 1 - trivy config . --exit-code 1
when: when:
- event: [push, pull_request] - event: [push, pull_request]
- name: notify - name: notify
image: dev.shielddagger.com/infra/discord-notifier image: dev.shielddagger.com/opensource/discord-notifier
failure: ignore failure: ignore
depends_on: depends_on:
- scan-repo - scan-repo
@@ -28,6 +36,6 @@ steps:
woodpecker_url: https://ci.shielddagger.com/api woodpecker_url: https://ci.shielddagger.com/api
woodpecker_token: woodpecker_token:
from_secret: woodpecker_token from_secret: woodpecker_token
icon_url: https://dev.shielddagger.com/repo-avatars/273e88fa2afde290121dc7b5987dc366b88325f147bf1e5766bca26296bbc1f9 icon_url: https://dev.shielddagger.com/repo-avatars/1a648399aed58ef1c440bde43fc3f0f98f237a8d8f68074febe98517a86e1887
when: when:
- status: [success, failure] - status: [success, failure]

View File

@@ -1,4 +1,4 @@
FROM python:3.12 as builder FROM python:3.12 AS builder
RUN pip install poetry RUN pip install poetry
@@ -14,7 +14,7 @@ RUN touch README.md
RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --without dev --no-root RUN --mount=type=cache,target=$POETRY_CACHE_DIR poetry install --without dev --no-root
FROM python:3.12-slim as runtime FROM python:3.12-slim AS runtime
RUN pip install poetry RUN pip install poetry

30
poetry.lock generated
View File

@@ -1,14 +1,14 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]] [[package]]
name = "certifi" name = "certifi"
version = "2024.2.2" version = "2024.8.30"
description = "Python package for providing Mozilla's CA Bundle." description = "Python package for providing Mozilla's CA Bundle."
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
] ]
[[package]] [[package]]
@@ -129,24 +129,24 @@ async = ["httpx (>=0.23.0,<0.24.0)"]
[[package]] [[package]]
name = "idna" name = "idna"
version = "3.7" version = "3.8"
description = "Internationalized Domain Names in Applications (IDNA)" description = "Internationalized Domain Names in Applications (IDNA)"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.6"
files = [ files = [
{file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"},
{file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"},
] ]
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.31.0" version = "2.32.3"
description = "Python HTTP for Humans." description = "Python HTTP for Humans."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.8"
files = [ files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
] ]
[package.dependencies] [package.dependencies]
@@ -161,13 +161,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]] [[package]]
name = "urllib3" name = "urllib3"
version = "2.2.1" version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more." description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
] ]
[package.extras] [package.extras]

View File

@@ -10,6 +10,13 @@ from urllib.parse import urljoin, urlparse
from requests import Session from requests import Session
from requests.models import Response from requests.models import Response
logfile_webhook = DiscordWebhook(environ["PLUGIN_WEBHOOK_URL"], rate_limit_retry=True)
logfile_webhook.username = "Woodpecker CI"
logfile_webhook.avatar_url = "https://ci.shielddagger.com/favicons/favicon-dark-error.png"
webhook = DiscordWebhook(environ["PLUGIN_WEBHOOK_URL"], rate_limit_retry=True)
webhook.username = "Woodpecker CI"
class APISession(Session): class APISession(Session):
def __init__(self, base_url) -> None: def __init__(self, base_url) -> None:
@@ -19,13 +26,44 @@ class APISession(Session):
def request(self, method: str | bytes, url: str | bytes, *args, **kwargs) -> Response: def request(self, method: str | bytes, url: str | bytes, *args, **kwargs) -> Response:
url = urljoin(self.base_url, url) url = urljoin(self.base_url, url)
return super().request(method, url, *args, **kwargs) return super().request(method, url, *args, **kwargs)
try:
ci_client = APISession(environ["PLUGIN_WOODPECKER_URL"].rstrip("/"))
ci_client.headers.setdefault("Authorization", f"Bearer {environ["PLUGIN_WOODPECKER_TOKEN"]}")
except KeyError:
webhook.add_embed(DiscordEmbed(
"API Error",
"API access has not been configured.\n"\
"As a result, Discord Notifier is unable to retrieve job information like logs or status.\n"\
"Please use the `woodpecker_url` and `woodpecker_token` settings to allow API access.",
color="ED4345",
timestamp=datetime.now(UTC),
footer={
"text": "Discord Notifier",
"icon_url": "https://dev.shielddagger.com/repo-avatars/273e88fa2afde290121dc7b5987dc366b88325f147bf1e5766bca26296bbc1f9"
}
))
webhook.execute()
exit(1)
logfile_webhook = None pipeline_url = urlparse(environ["CI_PIPELINE_URL"]).path.lstrip("/").split("/")
webhook = DiscordWebhook(environ["PLUGIN_WEBHOOK_URL"], rate_limit_retry=True) repo_id = pipeline_url[1]
pipeline_number = pipeline_url[3]
success = environ["CI_PIPELINE_STATUS"] != "failure" pipeline_info = ci_client.get(f"/api/repos/{repo_id}/pipelines/{pipeline_number}").json()
success = None
for workflow in pipeline_info["workflows"]:
if workflow["name"] != environ["CI_WORKFLOW_NAME"]:
continue
for step in workflow["children"]:
if step["state"] != "failure":
continue
loginfo = ci_client.get(f"/api/repos/{repo_id}/logs/{pipeline_number}/{step['id']}").json()
logdata = b""
for logline in loginfo:
logdata += b64decode(logline["data"]) + b"\n"
logfile_webhook.add_file(logdata, f"{step['name']}.ansi")
success = all(step["state"] != "failure" for step in workflow["children"])
webhook.username = "Woodpecker CI"
webhook.avatar_url = "https://ci.shielddagger.com/favicons/favicon-dark-success.png" if success else \ webhook.avatar_url = "https://ci.shielddagger.com/favicons/favicon-dark-success.png" if success else \
"https://ci.shielddagger.com/favicons/favicon-dark-error.png" "https://ci.shielddagger.com/favicons/favicon-dark-error.png"
@@ -39,8 +77,8 @@ webhook.add_embed(DiscordEmbed(
"url": environ["CI_REPO_URL"] "url": environ["CI_REPO_URL"]
}, },
footer={ footer={
"text": environ["CI_COMMIT_AUTHOR"], "text": environ.get("CI_COMMIT_AUTHOR", "Woodpecker"),
"icon_url": environ["CI_COMMIT_AUTHOR_AVATAR"] "icon_url": environ.get("CI_COMMIT_AUTHOR_AVATAR", "https://ci.shielddagger.com/favicons/favicon-dark-default.png")
}, },
thumbnail={ thumbnail={
"url": getenv("PLUGIN_ICON_URL"), "url": getenv("PLUGIN_ICON_URL"),
@@ -65,47 +103,10 @@ webhook.add_embed(DiscordEmbed(
}] }]
)) ))
try:
if not success:
logfile_webhook = DiscordWebhook(environ["PLUGIN_WEBHOOK_URL"], rate_limit_retry=True)
logfile_webhook.username = "Woodpecker CI"
logfile_webhook.avatar_url = "https://ci.shielddagger.com/favicons/favicon-dark-error.png"
with APISession(environ["PLUGIN_WOODPECKER_URL"].rstrip("/")) as client:
client.headers.setdefault("Authorization", f"Bearer {environ["PLUGIN_WOODPECKER_TOKEN"]}")
pipeline_url = urlparse(environ["CI_PIPELINE_URL"]).path.lstrip("/").split("/")
repo_id = pipeline_url[1]
pipeline_number = pipeline_url[3]
pipeline_info = client.get(f"/api/repos/{repo_id}/pipelines/{pipeline_number}").json()
for workflow in pipeline_info["workflows"]:
if workflow["name"] != environ["CI_WORKFLOW_NAME"]:
continue
for step in workflow["children"]:
if step["state"] != "failure":
continue
loginfo = client.get(f"/api/repos/{repo_id}/logs/{pipeline_number}/{step['id']}").json()
logdata = b""
for logline in loginfo:
logdata += b64decode(logline["data"])
logfile_webhook.add_file(logdata, f"{step['name']}.ansi")
except KeyError:
webhook.add_embed(DiscordEmbed(
"API Error",
"API access has not been configured.\n"\
"As a result, Discord Notifier is unable to retrieve failed job logs to attach here.\n"\
"Please use the `woodpecker_url` and `woodpecker_token` settings to allow API access.",
color="ED4345",
timestamp=datetime.now(UTC),
footer={
"text": "Discord Notifier",
"icon_url": "https://dev.shielddagger.com/repo-avatars/273e88fa2afde290121dc7b5987dc366b88325f147bf1e5766bca26296bbc1f9"
}
))
pprint(webhook.json) pprint(webhook.json)
webhook.execute() webhook.execute()
if logfile_webhook is not None: if not success:
pprint(logfile_webhook.json) pprint(logfile_webhook.json)
logfile_webhook.execute() logfile_webhook.execute()