Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"ignorePaths": ["**/node_modules/**", "**/vscode-extension/**", "**/.git/**", ".vscode", "megalinter", "package-lock.json", "report"],
"ignorePaths": [
"**/node_modules/**",
"**/vscode-extension/**",
"**/.git/**",
"**/.pnpm-lock.json",
".vscode",
"megalinter",
"package-lock.json",
"report"
],
"language": "en",
"noConfigSearch": true,
"words": ["megalinter", "oxsecurity"],
Expand Down
56 changes: 34 additions & 22 deletions .github/workflows/pr-checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,56 @@ name: PR Checks

on: pull_request

permissions: read-all
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true

permissions: {}

jobs:
mega-lint:
name: Code Quality (MegaLinter)
megalinter:
name: MegaLinter
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
fetch-depth: 0

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 24.x
cache: 'npm'
cache-dependency-path: ./package-lock.json

- name: Install TS Project dependencies
shell: bash
run: npm ci
working-directory: .
- name: MegaLinter
uses: oxsecurity/megalinter@0e3ce9b9c8c10effb9b269509cc47ca17cae31c7 # v9
env:
VALIDATE_ALL_CODEBASE: >-
${{
github.event_name == 'push' &&
github.ref == 'refs/heads/main'
}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Cache Docker images
uses: ScribeMD/docker-cache@0.5.0
# Upload MegaLinter artifacts
- name: Archive production artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
if: success() || failure()
with:
key: docker-${{ runner.os }}-megalinter-v9

- name: Lint Code (MegaLinter)
run: npm run lint
name: MegaLinter reports
include-hidden-files: 'true'
path: |
megalinter-reports
mega-linter.log

validate-ownership:
name: Validate Ownership
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false

- name: Validate Ownership
run: |
Expand Down
15 changes: 15 additions & 0 deletions .jscpd.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"threshold": 0,
"reporters": ["html", "markdown"],
"ignore": [
"**/node_modules/**",
"**/.git/**",
"**/.rbenv/**",
"**/.venv/**",
"**/*cache*/**",
"**/.github/**",
"**/.idea/**",
"**/report/**",
"**/*.svg"
]
}
8 changes: 8 additions & 0 deletions .mega-linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ DISABLE_LINTERS:
- REPOSITORY_KICS
- REPOSITORY_DEVSKIM
- SPELL_CSPELL
- REPOSITORY_OSV_SCANNER

# Uncomment if you want MegaLinter to detect errors but not block CI to pass
# DISABLE_ERRORS: true
Expand All @@ -36,3 +37,10 @@ PYTHON_BANDIT_ARGUMENTS:
YAML_YAMLLINT_CONFIG_FILE: .yamllint.yaml
JAVASCRIPT_DEFAULT_STYLE: prettier
FILTER_REGEX_EXCLUDE: (package-lock\.json)$

ACTION_ZIZMOR_UNSECURED_ENV_VARIABLES:
- GITHUB_TOKEN

PRE_COMMANDS:
- command: npm install @map-colonies/prettier-config
cwd: 'root'
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ README.md
node_modules
.release-please-manifest.json
.ruff_cache/
.mypy_cache/
3 changes: 2 additions & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"images/pgadmin": "1.0.0",
"images/scaler": "1.0.9",
"images/sftpgo": "1.0.0",
"images/sftpgo-node-exporter": "1.0.0"
"images/sftpgo-node-exporter": "1.0.0",
"images/grafana": "1.0.0"
}
36 changes: 36 additions & 0 deletions images/grafana/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# We use an ARG to dynamically set the base version from CI
ARG GRAFANA_VERSION=12.3.1

# --- Stage 1: Downloader ---
FROM alpine:3.19 AS downloader

# Prevents silent pipeline failures
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]

# Install tools needed to parse YAML and extract zips
RUN apk add --no-cache curl yq unzip

WORKDIR /downloads
COPY plugins.yaml .

# Parse the YAML file and download/extract each plugin
RUN mkdir -p /plugins && \
yq e '.plugins[] | .name + " " + .version' plugins.yaml | while read -r plugin version; do \
echo "Downloading $plugin version $version..."; \
curl -fL -o "/tmp/${plugin}.zip" "https://grafana.com/api/plugins/${plugin}/versions/${version}/download"; \
unzip -q "/tmp/${plugin}.zip" -d "/plugins/${plugin}"; \
rm "/tmp/${plugin}.zip"; \
done

# --- Stage 2: Final Image ---
FROM grafana/grafana:${GRAFANA_VERSION}

USER root

# Copy only the extracted plugins from the downloader stage
COPY --from=downloader /plugins /opt/grafana/plugins
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it with the default value removed the existing plugins I installed from the Docker and started all from the default plugins. Only customizing the plugins dir allowed me to deploy Grafana with the desired plugins


# Ensure Grafana owns the plugins directory
RUN chown -R 472:0 /opt/grafana/plugins

USER grafana
19 changes: 19 additions & 0 deletions images/grafana/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Grafana with Pre-installed Plugins

This is a customized container image of [Grafana](https://grafana.com/), extending the official image to include pre-installed plugins.

## What this container does

The default Grafana image allows installing plugins at runtime via environment variables (e.g., `GF_INSTALL_PLUGINS`). However, downloading plugins at runtime can increase startup time, introduce dependencies on external network availability, and fail in air-gapped environments.

This image extends `grafana/grafana` with the following adjustments:

- Uses a multi-stage build to download and extract plugins defined in `plugins.yaml`.
- Bakes the extracted plugins directly into the `/var/lib/grafana/plugins` directory of the final image.
- Ensures the correct permissions (`472:0`) are applied so Grafana can read the plugins seamlessly.

To update or add new plugins, modify the `plugins.yaml` file and rebuild the image.

## Base Image

- `grafana/grafana`
7 changes: 7 additions & 0 deletions images/grafana/plugins.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
plugins:
- name: redis-datasource
version: 2.2.0
- name: redis-app
version: 2.3.2
- name: magnesium-wordcloud-panel
version: 1.2.11
3 changes: 2 additions & 1 deletion ownership.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"pgadmin": "infra",
"scaler": "infra",
"sftpgo": "infra",
"sftpgo-node-exporter": "infra"
"sftpgo-node-exporter": "infra",
"grafana": "infra"
}
Loading