Skip to content
Merged
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
4 changes: 1 addition & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
!versions.lock.json
!LICENSE
!THIRD_PARTY_NOTICES.md
!licenses/
!licenses/**
!scripts/
scripts/*
!scripts/verify-assets.py
!root/
!root/**
!downloads/
downloads/*
!downloads/weflow.tar.gz
!downloads/alpha.deb
!downloads/wechat.deb
10 changes: 3 additions & 7 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Generated credentials and host UID/GID are filled by scripts/init.sh.
WEHARBOR_IMAGE=weharbor:0.1.0
CUSTOM_USER=weharbor
# Must not start with "-" (the Selkies base passes it to `openssl passwd`).
PASSWORD=
PUID=1000
PGID=1000
Expand All @@ -13,17 +14,12 @@ BIND_ADDRESS=127.0.0.1
HTTP_PORT=3000
HTTPS_PORT=3001
API_BIND_ADDRESS=127.0.0.1
WEFLOW_API_PORT=5031
API_PORT=5031

AUTO_START_WECHAT=true
AUTO_START_WEFLOW=true
AUTO_START_ALPHA=true
ENABLE_APP_SWITCHER=true
APP_SWITCHER_MODE=web
ENABLE_WINDOW_DEFAULTS=true
ENABLE_BROWSER_NOTIFICATIONS=true
SELKIES_ENABLE_BINARY_CLIPBOARD=true
ENABLE_WECHAT_AUTO_LOGIN=true
AUTO_LOGIN_DELAY=3
ENABLE_WECHAT_NIGHTLY_RESTART=false
WECHAT_NIGHTLY_STOP_TIME=23:30
WECHAT_NIGHTLY_START_TIME=01:30
20 changes: 17 additions & 3 deletions .github/workflows/image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ on:
branches: [main]
workflow_dispatch:
inputs:
weflow_download_url:
description: Trusted HTTPS URL for the original WeFlow archive
alpha_download_url:
description: Trusted HTTPS URL for the original Alpha deb
type: string
required: false
publish:
description: Publish the tested image to this repository's GHCR package
type: boolean
default: false
image_tag:
description: Extra image tag for a manual publish (not latest or edge), for example selkies2-alpha
type: string
required: false
permissions:
contents: read
packages: write
Expand All @@ -26,7 +30,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
env:
WEFLOW_DOWNLOAD_URL: ${{ inputs.weflow_download_url || vars.WEFLOW_DOWNLOAD_URL }}
ALPHA_DOWNLOAD_URL: ${{ inputs.alpha_download_url || vars.ALPHA_DOWNLOAD_URL }}
WECHAT_DOWNLOAD_URL: ${{ vars.WECHAT_DOWNLOAD_URL }}
PROJECT_SOURCE: ${{ github.server_url }}/${{ github.repository }}
steps:
Expand All @@ -43,6 +47,15 @@ jobs:
fi
printf 'RELEASE_VERSION=%s\n' "$version" >> "$GITHUB_ENV"
printf 'IMAGE_REPOSITORY=ghcr.io/%s\n' "${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
- name: Validate manual image tag
if: github.event_name == 'workflow_dispatch' && inputs.image_tag != ''
env:
IMAGE_TAG: ${{ inputs.image_tag }}
run: |
if [[ ! "$IMAGE_TAG" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$ || "$IMAGE_TAG" == latest || "$IMAGE_TAG" == edge ]]; then
printf 'Invalid manual image tag: %s\n' "$IMAGE_TAG" >&2
exit 1
fi
- name: Prepare pinned assets
# Cache only checksum-verified vendor archives, never a /config profile.
uses: actions/cache@v4
Expand All @@ -62,6 +75,7 @@ jobs:
type=ref,event=pr
type=semver,pattern={{version}}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=raw,value=${{ inputs.image_tag }},enable=${{ github.event_name == 'workflow_dispatch' && inputs.image_tag != '' }}
- name: Build
uses: docker/build-push-action@v7
with:
Expand Down
21 changes: 12 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# WeHarbor contributor notes

- Keep upstream WeChat and WeFlow application files unmodified.
- Reuse the pinned wechat-selkies minimal image; keep only our integration
files under root/. Avoid copying the complete upstream repository.
- Keep upstream WeChat and Alpha application files unmodified (install the
pinned debs with dpkg/apt; never patch /opt/wechat or /opt/Alpha).
- Build on the pinned plain LinuxServer.io Selkies base (debiantrixie digest
in versions.lock.json). Keep only our integration files under root/.
- versions.lock.json is the source of component versions and checksums.
- Only linux/amd64 is supported by the current WeFlow archive.
- Only linux/amd64 is supported (Alpha and WeChat are pinned amd64 debs).
- All runtime state belongs under /config; never check data/, .env,
package archives, credentials, or user databases into Git.
- Preserve browser authentication and the WeFlow API's own token authentication.
- Changes to initialization must preserve existing profiles and user settings.
- Preserve browser authentication and Alpha's own bearer-token API auth.
Alpha binds 127.0.0.1 only; nginx republishes it on ALPHA_API_PROXY_PORT
(5032) and rejects browser Origin headers.
- Changes to initialization must preserve existing profiles and user settings
(see init-weharbor-profile for the legacy profile migration).
- Run python3 scripts/check.py; for image changes, build and run
scripts/smoke-test.sh IMAGE with an empty, temporary profile.
- Keep the public WeFlow skill in skills/weflow-api self-contained and portable.
Credentials come from the user's environment or local token file. Verify
client behavior and installation when changing the skill or its scripts.
- Keep the public skill in skills/alpha-api self-contained and portable.
Credentials come from the user's environment or local token file.
- MIT covers this repository's integration code. Vendor components retain
their own licenses; see THIRD_PARTY_NOTICES.md.
73 changes: 40 additions & 33 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
# syntax=docker/dockerfile:1
ARG BASE_IMAGE=ghcr.io/nickrunning/wechat-selkies:0.0.14-minimal@sha256:32b9094316b0bd267dc7d1778116a91a6a349fce20bb8dd652dbb9b38e0ef6ee
FROM ${BASE_IMAGE} AS weflow-extracted
ARG TARGETARCH
COPY versions.lock.json /tmp/versions.lock.json
COPY scripts/verify-assets.py /tmp/verify-assets.py
COPY downloads/weflow.tar.gz /tmp/weflow.tar.gz
RUN test "${TARGETARCH:-amd64}" = amd64 \
&& python3 /tmp/verify-assets.py weflow \
--lock /tmp/versions.lock.json --asset /tmp/weflow.tar.gz \
--extract /opt/weflow

# Plain LinuxServer.io Selkies base (Debian trixie, Selkies 2.0.0, X11/Openbox).
ARG BASE_IMAGE=ghcr.io/linuxserver/baseimage-selkies:debiantrixie@sha256:5b3f10459933740554081efbca9c8d5e006faf5116b54ab40b7bfad7c8111849
FROM ${BASE_IMAGE} AS integration-files
COPY root/ /integration/
# A checkout copied from a private home can inherit restrictive directory modes.
Expand All @@ -18,62 +9,78 @@ RUN find /integration -type d -exec chmod 0755 {} + \
&& find /integration -type f -exec chmod 0644 {} +

FROM ${BASE_IMAGE}
ARG TARGETARCH
ARG PROJECT_VERSION=0.1.0
ARG PROJECT_SOURCE
ARG PROJECT_REVISION
ARG APT_MIRROR
LABEL org.opencontainers.image.title="WeHarbor" \
org.opencontainers.image.description="Self-hosted WeChat desktop, WeFlow APIs and message notifications in one container" \
org.opencontainers.image.description="Self-hosted WeChat desktop, Alpha data APIs and message notifications in one container" \
org.opencontainers.image.version="${PROJECT_VERSION}" \
org.opencontainers.image.source="${PROJECT_SOURCE}" \
org.opencontainers.image.revision="${PROJECT_REVISION}"

RUN if [ -n "${APT_MIRROR}" ]; then \
sed -i "s#http://archive.ubuntu.com/ubuntu/#${APT_MIRROR%/}/#g" /etc/apt/sources.list; \
# Desktop integration packages that wechat-selkies used to provide, plus the
# Secret Service Alpha needs for Electron safeStorage.
RUN test "${TARGETARCH:-amd64}" = amd64 \
&& sed -i -e '/^deb-src /d' \
-e 's#http://security.debian.org/debian-security/*#http://deb.debian.org/debian-security#' \
/etc/apt/sources.list \
&& if [ -n "${APT_MIRROR}" ]; then \
sed -i "s#http://deb.debian.org/#${APT_MIRROR%/}/#g" /etc/apt/sources.list; \
fi \
&& apt-get update -o Dir::Etc::sourcelist="sources.list" -o Dir::Etc::sourceparts="-" \
&& apt-get install -y --no-install-recommends pkexec \
&& echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-weharbor-retries \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
dbus-x11 gnome-keyring libsecret-1-0 inotify-tools stalonetray \
python3-dbus python3-xlib \
libatomic1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 \
libxcb-render-util0 libxcb-shape0 libxcb-xinerama0 libxcb-xkb1 \
libxkbcommon-x11-0 libnss3 libgbm1 libasound2t64 \
&& rm -rf /var/lib/apt/lists/*

COPY versions.lock.json /usr/share/weharbor/versions.lock.json
COPY scripts/verify-assets.py /tmp/verify-assets.py
COPY downloads/wechat.deb /tmp/wechat.deb
RUN python3 /tmp/verify-assets.py wechat \
COPY downloads/alpha.deb /tmp/alpha.deb
RUN apt-get update \
&& python3 /tmp/verify-assets.py wechat \
--lock /usr/share/weharbor/versions.lock.json --asset /tmp/wechat.deb --install \
&& rm /tmp/wechat.deb /tmp/verify-assets.py

COPY --from=weflow-extracted /opt/weflow /opt/weflow
RUN chmod -R a+rX /opt/weflow \
&& ln -s /opt/weflow/weflow /usr/local/bin/weflow \
&& cp /opt/weflow/resources/icon.png /usr/share/pixmaps/weflow.png
&& python3 /tmp/verify-assets.py alpha \
--lock /usr/share/weharbor/versions.lock.json --asset /tmp/alpha.deb --install \
&& rm -rf /var/lib/apt/lists/* /tmp/wechat.deb /tmp/alpha.deb /tmp/verify-assets.py \
&& icon="$(find /usr/share/icons/hicolor -name alpha.png | sort -V | tail -1)" \
&& cp "$icon" /usr/share/pixmaps/alpha.png \
&& cp /usr/share/icons/hicolor/128x128/apps/wechat.png /usr/share/selkies/www/icon.png

COPY --from=integration-files /integration/ /
COPY LICENSE THIRD_PARTY_NOTICES.md /usr/share/doc/weharbor/
COPY licenses/ /usr/share/doc/weharbor/licenses/
RUN test -f /defaults/default.conf \
&& chmod -R a+rX /usr/share/doc/weharbor \
&& sed -i '\|location SUBFOLDERfiles {|i\ include /defaults/notification-proxy.conf;' /defaults/default.conf \
&& sed -i '\|location SUBFOLDERapi {|i\ include /defaults/notification-proxy.conf;' /defaults/default.conf \
&& grep -q notification-proxy.conf /defaults/default.conf \
&& cat /defaults/alpha-api-proxy.conf >> /defaults/default.conf \
&& sed -i 's#</body>#<script src="./browser-notifications.js"></script></body>#' \
/usr/share/selkies/selkies-dashboard/index.html \
&& if [ -d /usr/share/selkies/web ]; then \
cp /usr/share/selkies/selkies-dashboard/browser-notifications.js \
/usr/share/selkies/web/browser-notifications.js; \
fi \
&& python3 /scripts/weharbor/install-web.py \
&& sed -i '/<dock>/,/<\/dock>/s/<noStrut>no<\/noStrut>/<noStrut>yes<\/noStrut>/' /etc/xdg/openbox/rc.xml \
&& ln -sfn /scripts/weharbor/wechat-launch.py /usr/bin/wechat \
&& chmod 0755 \
/etc/s6-overlay/s6-rc.d/svc-app-switcher/run \
/etc/s6-overlay/s6-rc.d/svc-polkitd/run \
/etc/s6-overlay/s6-rc.d/init-weharbor-profile/run \
/scripts/app-switcher/app-switcher.py \
/scripts/notifications/*.sh /scripts/notifications/*.py \
/scripts/weflow/*.sh /scripts/weharbor/* \
/scripts/alpha/*.sh /scripts/wechat/*.sh /scripts/weharbor/* \
/usr/local/bin/stalonetray /usr/local/bin/weharbor-health

ENV TITLE="WeHarbor|微港" \
SELKIES_UI_TITLE="WeHarbor|微港" \
TZ="Asia/Shanghai" \
LC_ALL="zh_CN.UTF-8" \
AUTO_START_WECHAT="true" \
AUTO_START_QQ="false" \
AUTO_START_WEFLOW="true" \
AUTO_START_ALPHA="true" \
ALPHA_API_PORT="5031" \
ALPHA_API_PROXY_PORT="5032" \
ENABLE_APP_SWITCHER="true" \
APP_SWITCHER_MODE="web" \
ENABLE_WINDOW_DEFAULTS="true" \
Expand Down
Loading
Loading