Initiate automated workflow

This commit is contained in:
selfhst-bot
2025-08-09 14:44:04 -04:00
parent d6d3d239b4
commit 43a7ed5b97
6 changed files with 106 additions and 209 deletions

98
.github/workflows/build-and-push.yml vendored Normal file
View File

@@ -0,0 +1,98 @@
name: Build and Push Docker Images
on:
push:
branches: [ main ]
paths:
- 'build/**'
- '.github/workflows/build-and-push.yml'
release:
types: [ published ]
workflow_dispatch:
inputs:
version:
description: 'Version tag (e.g., 2.0.0)'
required: true
default: 'latest'
env:
REGISTRY: ghcr.io
IMAGE_NAME: selfhst/icons
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Read version from file
id: version
run: |
VERSION=$(cat build/VERSION | tr -d '\n')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version from file: $VERSION"
# Extract semantic version parts
MAJOR=$(echo $VERSION | cut -d. -f1)
MINOR=$(echo $VERSION | cut -d. -f1-2)
echo "major_version=$MAJOR" >> $GITHUB_OUTPUT
echo "minor_version=$MINOR" >> $GITHUB_OUTPUT
echo "Major: $MAJOR, Minor: $MINOR"
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
# For releases, use the release tag
type=ref,event=tag
# For main branch pushes, create semantic version tags
type=raw,value=${{ steps.version.outputs.version }},enable={{is_default_branch}}
type=raw,value=${{ steps.version.outputs.major_version }},enable={{is_default_branch}}
type=raw,value=${{ steps.version.outputs.minor_version }},enable={{is_default_branch}}
type=raw,value=latest,enable={{is_default_branch}}
labels: |
org.opencontainers.image.title=selfh.st/icons
org.opencontainers.image.description=Self-hosted icon server with custom color support
org.opencontainers.image.version=${{ steps.version.outputs.version }}
org.opencontainers.image.source=https://github.com/${{ github.repository }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: ./build
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Output image info
run: |
echo "🐳 Images built and pushed:"
echo "${{ steps.meta.outputs.tags }}" | sed 's/^/ - /'
echo ""
echo "🏗️ Supported architectures:"
echo " - linux/amd64 (Intel/AMD x86_64)"
echo " - linux/arm64 (ARM 64-bit, Apple M1, Raspberry Pi 4)"
echo " - linux/arm/v7 (ARM 32-bit, Raspberry Pi 2/3)"

3
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.*
staging/
!/.gitignore
!/.gitignore
!/.github

101
README.md
View File

@@ -1,107 +1,18 @@
<div align="center">
<img width="400" src="https://cdn.jsdelivr.net/gh/selfhst/cdn/assets/site/logos/selfh-st-icons.svg" alt="selfh.st/icons Logo">
</div>
<br/>
<p align="center">
<a href="https://selfh.st/support/#/portal/support">
<img src="https://img.shields.io/badge/Stripe-5469d4?style=for-the-badge&logo=stripe&logoColor=ffffff" alt="Stripe"/></a>
<a href="https://ko-fi.com/selfhst">
<img src="https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white" alt="Ko-fi"/></a>
<a href="https://patreon.com/selfhst">
<img src="https://img.shields.io/badge/Patreon-000000?style=for-the-badge&logo=patreon&logoColor=white" alt="Patreon"/></a>
<a href="https://buymeacoffee.com/selfhst">
<img src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black" alt="Buy Me a Coffee"/></a>
</p>
<br/>
<div align="center">
<img width="800" style="border-radius: 8px;" src="https://cdn.jsdelivr.net/gh/selfhst/cdn/assets/site/screenshots/selfh-st-icons.png" alt="selfh.st/icons Screenshot">
</div>
<br/>
[selfh.st/icons](https://selfh.st/icons) is a collection of 4,500+ logos and icons for self-hosted (and non-self-hosted) software.
[selfh.st/icons](https://selfh.st/icons) is a collection of logos and icons for self-hosted (and non-self-hosted) software.
The collection is available for browsing via the directory at [selfh.st/icons](https://selfh.st/icons) and served to users directly from this repo using the jsDelivr content delivery network.
To self-host the collection, users can clone, download, or sync the repository with a tool such as [git-sync](https://github.com/AkashRajpurohit/git-sync) and serve it via a web server of their choosing (Caddy, NGINX, etc.).
To self-host the collection, users should refer to the [repository's wiki](https://github.com/selfhst/icons/wiki).
## Color Options
#### Custom Colors
By default, most SVG icons are available in three color formats:
By default, the directory provides SVG icons (when available) in standard, dark, and light colors. Users looking to display the icons using colors of their choice can become a [paid member of selfh.st](https://selfh.st/perks) or [deploy the collection themselves](https://github.com/selfhst/icons/wiki).
* **Standard**: The standard colors of an icon without any modifications.
* **Dark**: A modified version of an icon displayed entirely in black (```#000000```).
* **Light**: A modified version of an icon displayed entirely in white (```#FFFFFF```).
#### Requests
(Toggles to view icons by color type are available in the [directory hosted on the selfh.st website](https://selfh.st/icons).)
## Custom Colors
Because the dark and light versions of each icon are monochromatic, CSS can theoretically be leveraged to apply custom colors to the icons.
This only works, however, when the SVG code is embedded directly onto a webpage. Unfortunately, most [integrations](https://selfh.st/apps/?tag=selfh-st-icons) link to the icons via an `<img>` tag, which prevents styling from being overridden via CSS.
As a workaround, a lightweight self-hosted server has been published via Docker that utilizes a URL parameter for color conversion on the fly. Continue reading for further instructions.
### Deploying the Custom Color Container
* [Introduction](https://github.com/selfhst/icons#introduction)
* [Deploying the container](https://github.com/selfhst/icons#deployment)
* [Configuring a reverse proxy (optional)](https://github.com/selfhst/icons#reverse-proxy)
* [Linking to a custom icon](https://github.com/selfhst/icons#linking)
* [Changelog](https://github.com/selfhst/icons#changelog)
#### Introduction
The Docker image below allows users to host a local server that acts as a proxy between requests and jsDelivr. When a color parameter is detected in the URL, the server will intercept the requests, fill the SVG file with that color, and serve it to the user.
Once deployed, users can append ```?color=eeeeee``` to the end of a URL to specify a custom color (replacing ```eeeeee``` with any [hex color code](https://htmlcolorcodes.com/)).
#### Deployment
The container can be easily deployed via docker-compose with the following snippet:
```
selfhst-icons:
image: ghcr.io/selfhst/icons:latest
restart: unless-stopped
ports:
- 4050:4050
```
No volume mounts or environment variables are currently required.
#### Reverse Proxy
While out of the scope of this guide, many applications will require users to leverage HTTPS when linking to icons served from the container.
The process to proxy the container and icons is straightforward. A sample Caddyfile configuration has been provided for reference:
```
icons.selfh.st {
reverse_proxy selfhst-icons:4050
}
```
#### Linking
After the container has been deployed, users can easily link to any existing icon within the collection:
* ```https://icons.selfh.st/bookstack.svg```
* ```https://icons.selfh.st/bookstack.png```
* ```https://icons.selfh.st/bookstack-dark.webp```
To customize the color, users **must** link to the *standard* version of an SVG icon that has available monochromatic (dark/light) versions. To do so, append a custom URL parameter referencing any [hex color code](https://htmlcolorcodes.com/):
* ```https://icons.selfh.st/bookstack.svg?color=eeeeee```
* ```https://icons.selfh.st/bookstack.svg?color=439b68```
**Note the following:**
* Only the standard icons accept URL parameters (for example, ```bookstack-light.svg?color=fff000``` will not yield a different color.
* Only append the alpha-numeric portion of the hex color code to the URL. The server will append the ```#``` in the backend before passing it on for styling.
##### Changelog
* 2025-04-30: Initial release
Requests for new or updated icons can be made via the repository's [discussions](https://github.com/selfhst/icons/discussions). To ensure quality and consistency, pull requests will not be accepted from unapproved members.

View File

@@ -1,11 +0,0 @@
FROM node:18-alpine
WORKDIR /app
COPY package.json ./
RUN npm install
COPY server.js .
EXPOSE 4050
CMD ["node", "server.js"]

View File

@@ -1,13 +0,0 @@
{
"name": "test-repo",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"type": "module",
"dependencies": {
"express": "^4.18.2",
"node-fetch": "^3.3.0"
}
}

View File

@@ -1,89 +0,0 @@
import express from 'express'
import fetch from 'node-fetch'
import path from 'path'
const app = express()
const PORT = 4050
const CDN_ROOT = 'https://cdn.jsdelivr.net/gh/selfhst/icons'
const CDN_PATH = 'svg'
async function fileExists(url) {
try {
const resp = await fetch(url, { method: 'HEAD' });
return resp.ok;
} catch {
return false;
}
}
async function fetchAndPipe(url, res) {
const response = await fetch(url);
if (!response.ok) return res.status(404).send('File not found');
res.type(path.extname(url).slice(1));
response.body.pipe(res);
}
app.get('/*', async (req, res) => {
const urlPath = req.path;
const extMatch = urlPath.match(/\.(\w+)$/);
if (!extMatch)
return res.status(404).send('File extension missing');
const ext = extMatch[1].toLowerCase();
if (!['png', 'webp', 'svg'].includes(ext))
return res.status(404).send('Format not supported');
const filename = urlPath.slice(1);
const lowerFilename = filename.toLowerCase();
const isSuffix = lowerFilename.endsWith('-light.svg') || lowerFilename.endsWith('-dark.svg');
if (isSuffix) {
return fetchAndPipe(`${CDN_ROOT}/${CDN_PATH}/${filename}`, res);
}
let mainUrl;
if (ext === 'png') {
mainUrl = `${CDN_ROOT}/png/${filename}`;
} else if (ext === 'webp') {
mainUrl = `${CDN_ROOT}/webp/${filename}`;
} else if (ext === 'svg') {
mainUrl = `${CDN_ROOT}/svg/${filename}`;
} else {
mainUrl = null;
}
const hasColor = !!req.query['color'] && req.query['color'].trim() !== '';
if (ext === 'svg') {
if (hasColor) {
const baseName = filename.replace(/\.(png|webp|svg)$/, '');
const suffixUrl = `${CDN_ROOT}/${CDN_PATH}/${baseName}-light.svg`;
if (await fileExists(suffixUrl)) {
let svgContent = await fetch(suffixUrl).then(r => r.text());
const color = req.query['color'].startsWith('#') ? req.query['color'] : `#${req.query['color']}`;
svgContent = svgContent
.replace(/style="[^"]*fill:\s*#fff[^"]*"/gi, (match) => {
console.log('Replacing style fill:', match);
return match.replace(/fill:\s*#fff/gi, `fill:${color}`);
})
.replace(/fill="#fff"/gi, `fill="${color}"`);
return res.type('image/svg+xml').send(svgContent);
} else {
return fetchAndPipe(mainUrl, res);
}
} else {
return fetchAndPipe(mainUrl, res);
}
} else {
// PNG/WebP: serve directly
return fetchAndPipe(mainUrl, res);
}
});
app.get('/', (req, res) => {
res.send('Self-hosted icon server');
});
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});