mirror of
https://github.com/selfhst/icons.git
synced 2026-04-30 13:26:18 -04:00
Initiate automated workflow
This commit is contained in:
98
.github/workflows/build-and-push.yml
vendored
Normal file
98
.github/workflows/build-and-push.yml
vendored
Normal 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
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.*
|
||||
staging/
|
||||
!/.gitignore
|
||||
!/.gitignore
|
||||
!/.github
|
||||
101
README.md
101
README.md
@@ -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.
|
||||
@@ -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"]
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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}`);
|
||||
});
|
||||
Reference in New Issue
Block a user