Commit 2a420dbcd2

2a420dbcd2ecc545737f312e1ca5f53f18c8eac6

parent: 9af155d40a

Unsigned

cmc <hello@cleberg.net> · 2026-08-08 20:32 UTC

Fix a11y labels, Docker quoting, pin Actions to SHA, correct input type

Layout: unified · split

.github/workflows/release.yml +6 −6
@@ -20,15 +20,15 @@ jobs:
20 attestations: write 20 attestations: write
21 steps: 21 steps:
22 - name: Checkout 22 - name: Checkout
23 uses: actions/checkout@v7 23 uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
24 24
25 # The Dockerfile cross-compiles with Go's GOOS/GOARCH from $BUILDPLATFORM, 25 # The Dockerfile cross-compiles with Go's GOOS/GOARCH from $BUILDPLATFORM,
26 # so no QEMU emulation is needed for the arm64 image. 26 # so no QEMU emulation is needed for the arm64 image.
27 - name: Set up Buildx 27 - name: Set up Buildx
28 uses: docker/setup-buildx-action@v4 28 uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
29 29
30 - name: Log in to ${{ env.REGISTRY }} 30 - name: Log in to ${{ env.REGISTRY }}
31 uses: docker/login-action@v4 31 uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4
32 with: 32 with:
33 registry: ${{ env.REGISTRY }} 33 registry: ${{ env.REGISTRY }}
34 username: ${{ github.actor }} 34 username: ${{ github.actor }}
@@ -36,7 +36,7 @@ jobs:
36 36
37 - name: Derive tags and labels 37 - name: Derive tags and labels
38 id: meta 38 id: meta
39 uses: docker/metadata-action@v6 39 uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
40 with: 40 with:
41 images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 41 images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
42 # A semver tag v1.3.4 publishes 1.3.4, 1.3, 1 and latest. 42 # A semver tag v1.3.4 publishes 1.3.4, 1.3, 1 and latest.
@@ -53,7 +53,7 @@ jobs:
53 53
54 - name: Build and push 54 - name: Build and push
55 id: build 55 id: build
56 uses: docker/build-push-action@v7 56 uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
57 with: 57 with:
58 context: . 58 context: .
59 platforms: linux/amd64,linux/arm64 59 platforms: linux/amd64,linux/arm64
@@ -68,7 +68,7 @@ jobs:
68 cache-to: type=gha,mode=max 68 cache-to: type=gha,mode=max
69 69
70 - name: Attest build provenance 70 - name: Attest build provenance
71 uses: actions/attest-build-provenance@v4 71 uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
72 with: 72 with:
73 subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 73 subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
74 subject-digest: ${{ steps.build.outputs.digest }} 74 subject-digest: ${{ steps.build.outputs.digest }}
Dockerfile +2 −1
@@ -8,7 +8,8 @@ ARG VERSION=dev
8 8
9WORKDIR /build 9WORKDIR /build
10COPY . . 10COPY . .
11RUN CGO_ENABLED=0 GOARCH=${TARGETARCH} GOOS=${TARGETOS} go build -ldflags "-s -w -extldflags '-static' -X main.version=${VERSION}" && \ 11RUN CGO_ENABLED=0 GOARCH="${TARGETARCH}" GOOS="${TARGETOS}" \
12 go build -ldflags "-s -w -extldflags '-static' -X main.version=${VERSION}" && \
12 echo "skunkyart:x:10000:10000:SkunkyArt user:/:/sbin/nologin" > /etc/minimal-passwd && \ 13 echo "skunkyart:x:10000:10000:SkunkyArt user:/:/sbin/nologin" > /etc/minimal-passwd && \
13 echo "skunkyart:x:10000:" > /etc/minimal-group 14 echo "skunkyart:x:10000:" > /etc/minimal-group
14 15
app/cache_test.go +1 −1
@@ -278,7 +278,7 @@ func TestAgeMemCacheEvicts(t *testing.T) {
278 if got := memGet(cold); got != nil { 278 if got := memGet(cold); got != nil {
279 t.Errorf("cold entry survived aging: got %q, want nil", got) 279 t.Errorf("cold entry survived aging: got %q, want nil", got)
280 } 280 }
281 if got := memGet(hot); got == nil { 281 if memGet(hot) == nil {
282 t.Error("hot entry was evicted after a hit, want it kept") 282 t.Error("hot entry was evicted after a hit, want it kept")
283 } 283 }
284} 284}
app/wrapper.go +3 −3
@@ -89,7 +89,7 @@ func (s skunkyart) GRUser() {
89 89
90 folderid, _ := strconv.Atoi(s.Args.Get("folder")) 90 folderid, _ := strconv.Atoi(s.Args.Get("folder"))
91 91
92 if a := s.Args.Get("all"); a == "true" { 92 if s.Args.Get("all") == "true" {
93 all = true 93 all = true
94 } 94 }
95 95
@@ -115,7 +115,7 @@ func (s skunkyart) GRUser() {
115 }) 115 })
116 } else { 116 } else {
117 for _, x := range content.Content.Gruser.Page.Modules { 117 for _, x := range content.Content.Gruser.Page.Modules {
118 if l := len(x.ModuleData.Folders.Results); l != 0 { 118 if len(x.ModuleData.Folders.Results) != 0 {
119 var folders strings.Builder 119 var folders strings.Builder
120 folders.WriteString(`<h1 id="folders"><a href="#folder">#</a> Folders</h1><div class="folders"><br>`) 120 folders.WriteString(`<h1 id="folders"><a href="#folder">#</a> Folders</h1><div class="folders"><br>`)
121 for _, x := range x.ModuleData.Folders.Results { 121 for _, x := range x.ModuleData.Folders.Results {
@@ -309,7 +309,7 @@ func (s skunkyart) Search() {
309 } 309 }
310 } 310 }
311 311
312 if l := len(usernames); l != 0 { 312 if len(usernames) != 0 {
313 ss.List += `<div class="content plates">` 313 ss.List += `<div class="content plates">`
314 for x := range len(usernames) { 314 for x := range len(usernames) {
315 ss.List += BuildUserPlate(s.Host, usernames[x]) 315 ss.List += BuildUserPlate(s.Host, usernames[x])
static/html/about.htm +1 −1
@@ -1,5 +1,5 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html> 2<html lang="en">
3 {{template "head" .}} 3 {{template "head" .}}
4 <main> 4 <main>
5 {{template "header" .}} 5 {{template "header" .}}
static/html/daily.htm +1 −1
@@ -1,5 +1,5 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html> 2<html lang="en">
3 {{template "head" . }} 3 {{template "head" . }}
4 <main> 4 <main>
5 {{template "header" . }} 5 {{template "header" . }}
static/html/deviantion.htm +3 −3
@@ -1,10 +1,10 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html> 2<html lang="en">
3 {{template "head" . }} 3 {{template "head" . }}
4 <main> 4 <main>
5 {{template "header" . }} 5 {{template "header" . }}
6 <figure> 6 <figure>
7 <img src="{{.BasePath}}media/emojitar/{{.Templates.Deviation.Post.Deviation.Author.Username}}?type=a" width="30px"> 7 <img src="{{.BasePath}}media/emojitar/{{.Templates.Deviation.Post.Deviation.Author.Username}}?type=a" width="30px" alt="{{.Templates.Deviation.Post.Deviation.Author.Username}} avatar">
8 <span><strong><a href="{{.BasePath}}group_user?type=about&q={{.Templates.Deviation.Post.Deviation.Author.Username}}">{{.Templates.Deviation.Post.Deviation.Author.Username}}</a></strong> — {{if (.Templates.Deviation.Post.Deviation.DD)}} 8 <span><strong><a href="{{.BasePath}}group_user?type=about&q={{.Templates.Deviation.Post.Deviation.Author.Username}}">{{.Templates.Deviation.Post.Deviation.Author.Username}}</a></strong> — {{if (.Templates.Deviation.Post.Deviation.DD)}}
9 <span class="dd" title="Daily Deviation!"><b>{{.Templates.Deviation.Post.Deviation.Title}}</b></span> 9 <span class="dd" title="Daily Deviation!"><b>{{.Templates.Deviation.Post.Deviation.Title}}</b></span>
10 {{else}}{{.Templates.Deviation.Post.Deviation.Title}}{{end}} 10 {{else}}{{.Templates.Deviation.Post.Deviation.Title}}{{end}}
@@ -13,7 +13,7 @@
13 </span> 13 </span>
14 <br> 14 <br>
15 {{if (ne .Templates.Deviation.Post.IMG "")}} 15 {{if (ne .Templates.Deviation.Post.IMG "")}}
16 <a href="{{.Templates.Deviation.Post.IMG}}" title="open/download image"><img src="{{.Templates.Deviation.Post.IMG}}" width="50%"></a> 16 <a href="{{.Templates.Deviation.Post.IMG}}" title="open/download image"><img src="{{.Templates.Deviation.Post.IMG}}" width="50%" alt="{{.Templates.Deviation.Post.Deviation.Title}}"></a>
17 <br> 17 <br>
18 {{end}} 18 {{end}}
19 {{if (ne .Templates.Deviation.Tags "")}} 19 {{if (ne .Templates.Deviation.Tags "")}}
static/html/gruser.htm +5 −5
@@ -1,5 +1,5 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html> 2<html lang="en">
3 {{template "head" . }} 3 {{template "head" . }}
4 <main> 4 <main>
5 <header> 5 <header>
@@ -17,9 +17,9 @@
17 | <a href="group_user?q={{.Templates.GroupUser.GR.Owner.Username}}&type=gallery&atom=true">RSS</a> 17 | <a href="group_user?q={{.Templates.GroupUser.GR.Owner.Username}}&type=gallery&atom=true">RSS</a>
18 </h1> 18 </h1>
19 <form method="get" action="{{.BasePath}}search"> 19 <form method="get" action="{{.BasePath}}search">
20 <input type="gallery" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false"> 20 <input type="text" name="q" aria-label="Search query" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false">
21 <input type="hidden" name="usr" value="{{.Templates.GroupUser.GR.Owner.Username}}"> 21 <input type="hidden" name="usr" value="{{.Templates.GroupUser.GR.Owner.Username}}">
22 <select name="type"> 22 <select name="type" aria-label="Search type">
23 <option value="gallery">Gallery</option> 23 <option value="gallery">Gallery</option>
24 <option value="folders">Folders</option> 24 <option value="folders">Folders</option>
25 <option value="all">All</option> 25 <option value="all">All</option>
@@ -31,11 +31,11 @@
31 </header> 31 </header>
32 {{if eq .Type 'a'}} 32 {{if eq .Type 'a'}}
33 {{if and (and (ne .Templates.About.Nsfw true) (ne .Templates.GroupUser.About.BGMeta.NSFW true)) (ne .Templates.GroupUser.About.BG "")}} 33 {{if and (and (ne .Templates.About.Nsfw true) (ne .Templates.GroupUser.About.BGMeta.NSFW true)) (ne .Templates.GroupUser.About.BG "")}}
34 <a href="{{.Templates.GroupUser.About.BGMeta.Url}}" class="ubg"><img title="{{if ne .Templates.GroupUser.GR.Owner.Username .Templates.GroupUser.About.BGMeta.Author.Username}} 34 <a href="{{.Templates.GroupUser.About.BGMeta.Url}}" class="ubg"><img alt="Profile background" title="{{if ne .Templates.GroupUser.GR.Owner.Username .Templates.GroupUser.About.BGMeta.Author.Username}}
35 {{.Templates.GroupUser.About.BGMeta.Author.Username}} - {{end}}{{.Templates.GroupUser.About.BGMeta.Title}}" src="{{.Templates.GroupUser.About.BG}}"></a> 35 {{.Templates.GroupUser.About.BGMeta.Author.Username}} - {{end}}{{.Templates.GroupUser.About.BGMeta.Title}}" src="{{.Templates.GroupUser.About.BG}}"></a>
36 {{end}} 36 {{end}}
37 37
38 <img src="{{.BasePath}}media/emojitar/{{.Templates.GroupUser.GR.Owner.Username}}?type=a" width="30px" height="30px"> <b>{{.Templates.GroupUser.GR.Owner.Username}}</b> 38 <img src="{{.BasePath}}media/emojitar/{{.Templates.GroupUser.GR.Owner.Username}}?type=a" width="30px" height="30px" alt="{{.Templates.GroupUser.GR.Owner.Username}} avatar"> <b>{{.Templates.GroupUser.GR.Owner.Username}}</b>
39 {{if (eq .Templates.GroupUser.About.A.Gender "male")}} 39 {{if (eq .Templates.GroupUser.About.A.Gender "male")}}
40 ♂️ 40 ♂️
41 {{end}} 41 {{end}}
static/html/head.htm +1 −1
@@ -21,6 +21,6 @@
21 <meta name="referrer" content="no-referrer" /> 21 <meta name="referrer" content="no-referrer" />
22 <link rel="stylesheet" href="{{.BasePath}}stylesheet"> 22 <link rel="stylesheet" href="{{.BasePath}}stylesheet">
23 <link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico"> 23 <link rel="icon" type="image/x-icon" href="{{.BasePath}}favicon.ico">
24 <meta name="viewport" content="width=device-width, height=device-height, initial-scale=0.4, user-scalable=no"/> 24 <meta name="viewport" content="width=device-width, height=device-height, initial-scale=0.4"/>
25</head> 25</head>
26{{end}}\ No newline at end of file 26{{end}}\ No newline at end of file
static/html/header.htm +2 −2
@@ -2,8 +2,8 @@
2<header> 2<header>
3 <h1><a href="">HOME</a> | <a href="dd">DD</a> {{if eq .Endpoint "dd"}}| <a href="{{.Endpoint}}?atom=true">RSS</a>{{end}}</h1> 3 <h1><a href="">HOME</a> | <a href="dd">DD</a> {{if eq .Endpoint "dd"}}| <a href="{{.Endpoint}}?atom=true">RSS</a>{{end}}</h1>
4 <form method="get" action="search"> 4 <form method="get" action="search">
5 <input type="text" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false" value="{{.QueryRaw}}"> 5 <input type="text" name="q" aria-label="Search query" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false" value="{{.QueryRaw}}">
6 <select name="type"> 6 <select name="type" aria-label="Search type">
7 <option value="all">All</option> 7 <option value="all">All</option>
8 <option value="tag">Tag</option> 8 <option value="tag">Tag</option>
9 <option value="r">Groups</option> 9 <option value="r">Groups</option>
static/html/index.htm +5 −5
@@ -1,10 +1,10 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html> 2<html lang="en">
3 <head> 3 <head>
4 <title>SkunkyArt</title> 4 <title>SkunkyArt</title>
5 <link rel="stylesheet" href="{{.}}stylesheet"/> 5 <link rel="stylesheet" href="{{.}}stylesheet"/>
6 <link rel="icon" type="image/x-icon" href="{{.}}favicon.ico"> 6 <link rel="icon" type="image/x-icon" href="{{.}}favicon.ico">
7 <meta name="viewport" content="width=device-width, height=device-height, initial-scale=0.4, user-scalable=no; user-scalable=0"/> 7 <meta name="viewport" content="width=device-width, height=device-height, initial-scale=0.4"/>
8 8
9 <style> 9 <style>
10 main { 10 main {
@@ -70,12 +70,12 @@
70 </style> 70 </style>
71 </head> 71 </head>
72 <main> 72 <main>
73 <img src="{{.}}favicon.ico" title="SkunkyArt logo" draggable="false"> 73 <img src="{{.}}favicon.ico" title="SkunkyArt logo" alt="SkunkyArt logo" draggable="false">
74 <div> 74 <div>
75 <h1><a href="{{.}}dd">Daily Deviations</a> | <a href="{{.}}about">About</a></h1> 75 <h1><a href="{{.}}dd">Daily Deviations</a> | <a href="{{.}}about">About</a></h1>
76 <form method="get" action="{{.}}search"> 76 <form method="get" action="{{.}}search">
77 <input type="text" name="q" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false"> 77 <input type="text" name="q" aria-label="Search query" placeholder="Search for ..." autocomplete="off" autocapitalize="none" spellcheck="false">
78 <select name="type"> 78 <select name="type" aria-label="Search type">
79 <option value="all">All</option> 79 <option value="all">All</option>
80 <option value="tag">Tag</option> 80 <option value="tag">Tag</option>
81 <option value="r">Groups</option> 81 <option value="r">Groups</option>
static/html/search.htm +1 −1
@@ -1,5 +1,5 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html> 2<html lang="en">
3 {{template "head" . }} 3 {{template "head" . }}
4 <main> 4 <main>
5 {{template "header" . }} 5 {{template "header" . }}