Skip to content

Commit baebcaf

Browse files
committed
Add markdownlint and shellcheck Docker images
These replace super-linter for markdown and shell script linting across Pony projects. super-linter v3.8.3 pulls a 2GB+ image containing dozens of linters when we only need two. These dedicated images are small, fast, and follow the existing actionlint pattern. Both images have entrypoint wrappers for fire-and-go usage — consumers just `uses: docker://ghcr.io/ponylang/shared-docker-ci-<tool>:<tag>` with no args. The markdownlint entrypoint reads .markdownlintignore and converts entries to negation patterns since markdownlint-cli2 doesn't read that file natively. The shellcheck entrypoint finds and lints all .sh/.bash files automatically. A workflow_dispatch workflow builds all three linter images (actionlint, markdownlint, shellcheck) from the GitHub UI.
1 parent 899d7e2 commit baebcaf

8 files changed

Lines changed: 253 additions & 0 deletions

File tree

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
name: Build linter images
2+
3+
on:
4+
workflow_dispatch:
5+
6+
permissions:
7+
contents: read
8+
packages: write
9+
10+
jobs:
11+
actionlint:
12+
name: Build and push actionlint
13+
runs-on: ubuntu-latest
14+
15+
concurrency:
16+
group: actionlint
17+
cancel-in-progress: true
18+
19+
steps:
20+
- uses: actions/checkout@v6.0.2
21+
- name: Login to GitHub Container Registry
22+
# v2.2.0
23+
uses: docker/login-action@v4
24+
with:
25+
registry: ghcr.io
26+
username: ${{ github.repository_owner }}
27+
password: ${{ secrets.GITHUB_TOKEN }}
28+
- name: Build and push
29+
run: bash actionlint/build-and-push.bash
30+
- name: Send alert on failure
31+
if: ${{ failure() }}
32+
uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa
33+
with:
34+
api-key: ${{ secrets.ZULIP_SCHEDULED_JOB_FAILURE_API_KEY }}
35+
email: ${{ secrets.ZULIP_SCHEDULED_JOB_FAILURE_EMAIL }}
36+
organization-url: 'https://ponylang.zulipchat.com/'
37+
to: notifications
38+
type: stream
39+
topic: ${{ github.repository }} scheduled job failure
40+
content: ${{ github.server_url}}/${{ github.repository }}/actions/runs/${{ github.run_id }} failed.
41+
42+
markdownlint:
43+
name: Build and push markdownlint
44+
runs-on: ubuntu-latest
45+
46+
concurrency:
47+
group: markdownlint
48+
cancel-in-progress: true
49+
50+
steps:
51+
- uses: actions/checkout@v6.0.2
52+
- name: Login to GitHub Container Registry
53+
# v2.2.0
54+
uses: docker/login-action@v4
55+
with:
56+
registry: ghcr.io
57+
username: ${{ github.repository_owner }}
58+
password: ${{ secrets.GITHUB_TOKEN }}
59+
- name: Build and push
60+
run: bash markdownlint/build-and-push.bash
61+
- name: Send alert on failure
62+
if: ${{ failure() }}
63+
uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa
64+
with:
65+
api-key: ${{ secrets.ZULIP_SCHEDULED_JOB_FAILURE_API_KEY }}
66+
email: ${{ secrets.ZULIP_SCHEDULED_JOB_FAILURE_EMAIL }}
67+
organization-url: 'https://ponylang.zulipchat.com/'
68+
to: notifications
69+
type: stream
70+
topic: ${{ github.repository }} scheduled job failure
71+
content: ${{ github.server_url}}/${{ github.repository }}/actions/runs/${{ github.run_id }} failed.
72+
73+
shellcheck:
74+
name: Build and push shellcheck
75+
runs-on: ubuntu-latest
76+
77+
concurrency:
78+
group: shellcheck
79+
cancel-in-progress: true
80+
81+
steps:
82+
- uses: actions/checkout@v6.0.2
83+
- name: Login to GitHub Container Registry
84+
# v2.2.0
85+
uses: docker/login-action@v4
86+
with:
87+
registry: ghcr.io
88+
username: ${{ github.repository_owner }}
89+
password: ${{ secrets.GITHUB_TOKEN }}
90+
- name: Build and push
91+
run: bash shellcheck/build-and-push.bash
92+
- name: Send alert on failure
93+
if: ${{ failure() }}
94+
uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa
95+
with:
96+
api-key: ${{ secrets.ZULIP_SCHEDULED_JOB_FAILURE_API_KEY }}
97+
email: ${{ secrets.ZULIP_SCHEDULED_JOB_FAILURE_EMAIL }}
98+
organization-url: 'https://ponylang.zulipchat.com/'
99+
to: notifications
100+
type: stream
101+
topic: ${{ github.repository }} scheduled job failure
102+
content: ${{ github.server_url}}/${{ github.repository }}/actions/runs/${{ github.run_id }} failed.
103+
104+
prune-untagged-images:
105+
needs:
106+
- actionlint
107+
- markdownlint
108+
- shellcheck
109+
110+
name: Prune untagged images
111+
runs-on: ubuntu-latest
112+
113+
strategy:
114+
fail-fast: false
115+
matrix:
116+
image:
117+
- shared-docker-ci-actionlint
118+
- shared-docker-ci-markdownlint
119+
- shared-docker-ci-shellcheck
120+
121+
steps:
122+
- name: Prune
123+
# v5.0.0
124+
uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55
125+
with:
126+
package-name: ${{ matrix.image }}
127+
package-type: 'container'
128+
min-versions-to-keep: 1
129+
delete-only-untagged-versions: 'true'
130+
- name: Send alert on failure
131+
if: ${{ failure() }}
132+
uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa
133+
with:
134+
api-key: ${{ secrets.ZULIP_SCHEDULED_JOB_FAILURE_API_KEY }}
135+
email: ${{ secrets.ZULIP_SCHEDULED_JOB_FAILURE_EMAIL }}
136+
organization-url: 'https://ponylang.zulipchat.com/'
137+
to: notifications
138+
type: stream
139+
topic: ${{ github.repository }} scheduled job failure
140+
content: ${{ github.server_url}}/${{ github.repository }}/actions/runs/${{ github.run_id }} failed.

.github/workflows/pr.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,19 @@ jobs:
148148
uses: docker/setup-buildx-action@v4
149149
- name: Test build
150150
run: "docker buildx build --platform linux/arm64,linux/amd64 --pull --file=standard-builder-with-pcre/Dockerfile ."
151+
152+
validate-markdownlint-image-builds:
153+
name: Validate markdownlint Docker image builds
154+
runs-on: ubuntu-latest
155+
steps:
156+
- uses: actions/checkout@v6.0.2
157+
- name: Docker build
158+
run: "docker build --pull markdownlint/"
159+
160+
validate-shellcheck-image-builds:
161+
name: Validate shellcheck Docker image builds
162+
runs-on: ubuntu-latest
163+
steps:
164+
- uses: actions/checkout@v6.0.2
165+
- name: Docker build
166+
run: "docker build --pull shellcheck/"

markdownlint/Dockerfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM node:22-alpine
2+
3+
RUN npm install -g markdownlint-cli2@0.17.2 && \
4+
npm cache clean --force && \
5+
rm -rf /root/.npm
6+
7+
COPY entrypoint.sh /entrypoint.sh
8+
RUN chmod +x /entrypoint.sh
9+
10+
ENTRYPOINT ["/entrypoint.sh"]

markdownlint/build-and-push.bash

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
#
7+
# *** You should already be logged in to GitHub Container Registry when you run
8+
# this ***
9+
#
10+
11+
NAME="ponylang/shared-docker-ci-markdownlint"
12+
TODAY=$(date +%Y%m%d)
13+
DOCKERFILE_DIR="$(dirname "$0")"
14+
15+
# GitHub Container Registry
16+
NAME="ghcr.io/${NAME}"
17+
docker build --pull -t "${NAME}:${TODAY}" "${DOCKERFILE_DIR}"
18+
docker push "${NAME}:${TODAY}"

markdownlint/entrypoint.sh

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
if [ $# -gt 0 ]; then
6+
exec markdownlint-cli2 "$@"
7+
fi
8+
9+
set -- "**/*.md"
10+
11+
if [ -f .markdownlintignore ]; then
12+
while IFS= read -r line || [ -n "$line" ]; do
13+
case "$line" in
14+
''|'#'*) continue ;;
15+
esac
16+
set -- "$@" "#${line%/}"
17+
done < .markdownlintignore
18+
fi
19+
20+
exec markdownlint-cli2 "$@"

shellcheck/Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM alpine:3.23 AS builder
2+
3+
RUN cd /tmp && \
4+
wget https://github.com/koalaman/shellcheck/releases/download/v0.10.0/shellcheck-v0.10.0.linux.x86_64.tar.xz && \
5+
tar xf shellcheck-v0.10.0.linux.x86_64.tar.xz && \
6+
cp shellcheck-v0.10.0/shellcheck /usr/bin/ && \
7+
chmod a+x /usr/bin/shellcheck
8+
9+
FROM alpine:3.23
10+
11+
COPY --from=builder /usr/bin/shellcheck /usr/bin/shellcheck
12+
COPY entrypoint.sh /entrypoint.sh
13+
RUN chmod +x /entrypoint.sh
14+
15+
ENTRYPOINT ["/entrypoint.sh"]

shellcheck/build-and-push.bash

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#!/bin/bash
2+
3+
set -o errexit
4+
set -o nounset
5+
6+
#
7+
# *** You should already be logged in to GitHub Container Registry when you run
8+
# this ***
9+
#
10+
11+
NAME="ponylang/shared-docker-ci-shellcheck"
12+
TODAY=$(date +%Y%m%d)
13+
DOCKERFILE_DIR="$(dirname "$0")"
14+
15+
# GitHub Container Registry
16+
NAME="ghcr.io/${NAME}"
17+
docker build --pull -t "${NAME}:${TODAY}" "${DOCKERFILE_DIR}"
18+
docker push "${NAME}:${TODAY}"

shellcheck/entrypoint.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
if [ $# -gt 0 ]; then
6+
shellcheck "$@"
7+
else
8+
files=$(find . -type f \( -name '*.sh' -o -name '*.bash' \))
9+
10+
if [ -z "$files" ]; then
11+
echo "No shell scripts found."
12+
exit 0
13+
fi
14+
15+
echo "$files" | xargs shellcheck
16+
fi

0 commit comments

Comments
 (0)