perf(runner): Reduce CI buildtimes (#174)

This is an effort to reduce build times, for the runner image

---------

Co-authored-by: Kristian Ollikainen <14197772+DatCaptainHorse@users.noreply.github.com>
This commit is contained in:
Wanjohi
2025-01-31 15:24:37 +03:00
committed by GitHub
parent c14626b104
commit 29bc44ab83
18 changed files with 601 additions and 1655 deletions

3
.dockerignore Normal file
View File

@@ -0,0 +1,3 @@
**/target/
**/.git
**/.env

View File

@@ -6,7 +6,6 @@ on:
pull_request:
paths:
- "containers/runner.Containerfile"
- "packages/server/**"
- "packages/scripts/**"
- ".github/workflows/runner.yml"
schedule:
@@ -16,7 +15,7 @@ on:
paths:
- "containers/runner.Containerfile"
- ".github/workflows/runner.yml"
- "packages/server/**"
- "packages/scripts/**"
tags:
- v*.*.*
release:
@@ -51,15 +50,22 @@ jobs:
name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10
swap-size-gb: 20
-
name: Cache Docker layers
name: Cache Rust artifacts
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-
restore-keys: |
${{ runner.os }}-buildx-${{ github.sha }}
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
-
name: Cache mold linker
uses: actions/cache@v3
with:
path: /usr/bin/mold
key: ${{ runner.os }}-mold
-
name: Build Docker image
uses: docker/build-push-action@v5
@@ -68,14 +74,7 @@ jobs:
context: ./
push: false
load: true
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
tags: nestri:runner
# -
# name: Move cache
# run: |
# rm -rf /tmp/.buildx-cache
# mv /tmp/.buildx-cache-new /tmp/.buildx-cache
build-docker-main:
name: Build image on main
@@ -116,15 +115,22 @@ jobs:
name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 10
swap-size-gb: 20
-
name: Cache Docker layers
name: Cache Rust artifacts
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-
restore-keys: |
${{ runner.os }}-buildx-${{ github.sha }}
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
-
name: Cache mold linker
uses: actions/cache@v3
with:
path: /usr/bin/mold
key: ${{ runner.os }}-mold
-
name: Build Docker image
uses: docker/build-push-action@v5
@@ -132,12 +138,5 @@ jobs:
file: containers/runner.Containerfile
context: ./
push: true
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# -
# name: Move cache
# run: |
# rm -rf /tmp/.buildx-cache
# mv /tmp/.buildx-cache-new /tmp/.buildx-cache

4
.gitignore vendored
View File

@@ -52,4 +52,6 @@ id_*
target
tmp
.partykit
.partykit
key_*

0
.npmrc
View File

View File

@@ -1,29 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate

View File

@@ -1,120 +0,0 @@
# Contributing to Nestri
First off, thank you for considering contributing to Nestri! It's people like you that make Nestri such a great tool.
## Code of Conduct
By participating in this project, you are expected to uphold our [Code of Conduct](CODE_OF_CONDUCT.md).
## How Can I Contribute?
### Reporting Bugs
This section guides you through submitting a bug report for Nestri. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
- Use a clear and descriptive title for the issue to identify the problem.
- Describe the exact steps which reproduce the problem in as many details as possible.
- Provide specific examples to demonstrate the steps.
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for Nestri, including completely new features and minor improvements to existing functionality.
- Use a clear and descriptive title for the issue to identify the suggestion.
- Provide a step-by-step description of the suggested enhancement in as many details as possible.
- Provide specific examples to demonstrate the steps.
### Pull Requests
- Fill in the required template
- Do not include issue numbers in the PR title
- Include screenshots and animated GIFs in your pull request whenever possible.
- Follow the JavaScript/TypeScript styleguides.
- End all files with a newline
## Styleguides
### Git Commit Messages
- Use the present tense ("Add feature" not "Added feature")
- Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
- Limit the first line to 72 characters or less
- Reference issues and pull requests liberally after the first line
### JavaScript/Typescript Styleguide
All JavaScript and Typescript must adhere to the eslint and TS rules set in `.eslintrc` and `tsconfig` respectively. Your build will fail, otherwise.
## Additional Notes
### Issue and Pull Request Labels
This section lists the labels we use to help us track and manage issues and pull requests.
* `🐛 fix` - Issues that are bugs.
* `✨ feat` - Issues that are feature requests.
* `📝 docs` - Issues or pull requests related to documentation.
* `🔧 chore` - Pull requests that add or update configuration files
* `💄 style` - Issues or Pull requests related to the UI or style files
* `⚡ perf` - Issues or Pull Requests that are related to performance
* `♻ refactor` - Issues or Pull Requests related to code refactors
* `good first issue` - Good for newcomers.
## Project Structure
Nestri is organized as a monorepo using Turborepo. Here's an overview of the main directories and their purposes:
### Root Directory
- `apps/`: Contains the main applications
- `www/`: The main Nestri website built with Qwik
- `infra/`: Contains the relevant files to deploy the app using [SST](https://sst.dev)
- `packages/`: Shared packages and configurations
- `api/`: Core API for Nestri
- `eslint-config/`: Shared ESLint configurations
- `typescript-config/`: Shared TypeScript configurations
- `ui/`: Shared UI components and styles
### Key Files
- `package.json`: Root package file defining workspaces and shared dev dependencies
- `turbo.json`: Turborepo configuration
- `LICENSE`: GNU Affero General Public License v3.0
### Apps
#### www (Nestri Website)
This is the Nestri website hosted on Cloudflare Pages
### Packages
#### api (Nestri Core)
The core API for Nestri, built with Hono and deployed to Cloudflare Workers.
#### eslint-config
Shared ESLint configurations for maintaining consistent code style across the project.
#### typescript-config
Shared TypeScript configurations to ensure consistent TypeScript settings across the project.
#### ui
Shared UI components and styles used across the Nestri project.
### Infrastructure
- `infra/`: Contains infrastructure-as-code files
- `www.ts`: Defines the deployment configuration for the Nestri website
### Development
When working on Nestri, you'll primarily be dealing with the `apps/www` directory for the main website and the various packages in the `packages/` directory for shared functionality.
For more detailed information about each package or app, refer to their respective README files or package.json scripts.
Thank you for contributing to Nestri!

View File

@@ -1,17 +0,0 @@
[workspace]
resolver = "2"
members = [
"packages/server",
"packages/relay/dev"
]
[workspace.package]
version = "0.1.0-alpha.1"
repository = "https://github.com/nestriness/nestri"
edition = "2021"
rust-version = "1.80"
[workspace.dependencies]
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main", features = ["v1_24"] }
gst-webrtc = { package = "gstreamer-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main", features = ["v1_24"] }
gstrswebrtc = { package = "gst-plugin-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs", branch = "main", features = ["v1_22"] }

View File

@@ -2,173 +2,252 @@
ARG BASE_IMAGE=docker.io/cachyos/cachyos:latest
#******************************************************************************
# nestri-server-builder
# base-builder
#******************************************************************************
FROM ${BASE_IMAGE} AS gst-builder
WORKDIR /builder/
FROM ${BASE_IMAGE} AS base-builder
# Grab build and rust packages #
RUN pacman -Sy --noconfirm meson pkgconf cmake git gcc make rustup \
gstreamer gst-plugins-base gst-plugins-good gst-plugin-rswebrtc
# Setup stable rust toolchain #
RUN rustup default stable
#Copy the whole repo inside the build container
# COPY ./ /builder/nestri/
# Install mold linker and sccache upfront
RUN pacman -Sy --noconfirm mold && \
pacman -S --noconfirm rust && \
cargo install -j $(nproc) --root /usr/local sccache
RUN --mount=type=cache,target=/usr/local/cargo/registry \
CARGO_HOME=/usr/local/cargo \
cargo install --locked cargo-chef
ENV ARTIFACTS=/artifacts
RUN mkdir -p /artifacts
#******************************************************************************
# nestri-server-builder
#******************************************************************************
# FROM base-builder AS nestri-server-builder
# WORKDIR /builder/
RUN --mount=type=bind,target=/builder/nestri/,rw \
--mount=type=cache,target=/builder/nestri/target/ \
--mount=type=cache,target=/usr/local/cargo/git/db \
--mount=type=cache,target=/usr/local/cargo/registry/ \
cd /builder/nestri/packages/server/ \
&& cargo build --release \
&& cp /builder/nestri/target/release/nestri-server /artifacts/
# RUN pacman -Sy --noconfirm meson pkgconf cmake git gcc make \
# gstreamer gst-plugins-base gst-plugins-good gst-plugin-rswebrtc
#******************************************************************************
# gstwayland-builder
# nestri-server-planner
#******************************************************************************
FROM ${BASE_IMAGE} AS gstwayland-builder
# FROM nestri-server-builder AS nestri-server-planner
# WORKDIR /builder/nestri/
# COPY packages/server/Cargo.toml packages/server/Cargo.lock ./
# RUN --mount=type=cache,target=/root/.cache/sccache \
# --mount=type=cache,target=/usr/local/cargo/registry \
# --mount=type=cache,target=/tmp \
# export RUSTC_WRAPPER=/usr/local/bin/sccache && \
# cargo chef prepare --recipe-path recipe.json
#******************************************************************************
# nestri-server-cacher
#******************************************************************************
# FROM nestri-server-builder AS nestri-server-cacher
# WORKDIR /builder/nestri/
# COPY --from=nestri-server-planner /builder/nestri/recipe.json .
# RUN --mount=type=cache,target=/root/.cache/sccache \
# --mount=type=cache,target=/usr/local/cargo/registry \
# --mount=type=cache,target=/tmp \
# export RUSTC_WRAPPER=/usr/local/bin/sccache && \
# cargo chef cook --release --recipe-path recipe.json
#******************************************************************************
# nestri-server-build
#******************************************************************************
# FROM nestri-server-builder AS nestri-server-build
# WORKDIR /builder/nestri/
# COPY --from=nestri-server-cacher /builder/nestri/target target
# COPY packages/server/ ./packages/server/
# RUN --mount=type=cache,target=/root/.cache/sccache \
# --mount=type=cache,target=/usr/local/cargo/registry \
# --mount=type=cache,target=/tmp \
# export RUSTC_WRAPPER=/usr/local/bin/sccache && \
# export CARGO_BUILD_JOBS=$(nproc) && \
# export RUSTFLAGS="-C link-arg=-fuse-ld=mold -C target-cpu=native" && \
# cargo build --release && \
# cp target/release/nestri-server "$ARTIFACTS"
#******************************************************************************
# gst-wayland-builder
#******************************************************************************
FROM base-builder AS gst-wayland-builder
WORKDIR /builder/
# Grab build and rust packages #
RUN pacman -Sy --noconfirm meson pkgconf cmake git gcc make rustup \
libxkbcommon wayland gstreamer gst-plugins-base gst-plugins-good libinput
RUN pacman -Sy --noconfirm meson pkgconf cmake git gcc make \
libxkbcommon wayland gstreamer gst-plugins-base gst-plugins-good libinput
# Setup stable rust toolchain #
RUN rustup default stable
# Build required cargo-c package #
RUN --mount=type=cache,target=/usr/local/cargo/git/db \
--mount=type=cache,target=/usr/local/cargo/registry/ \
--mount=type=cache,target=/root/.cargo/bin/ \
cargo install cargo-c
RUN --mount=type=cache,target=/usr/local/cargo/registry \
CARGO_HOME=/usr/local/cargo \
cargo install --locked cargo-c
# Clone gst plugin source #
RUN git clone https://github.com/games-on-whales/gst-wayland-display.git
# Build gst plugin #
RUN mkdir plugin
RUN mkdir -p /artifacts
#******************************************************************************
# gst-wayland-planner
#******************************************************************************
FROM gst-wayland-builder AS gst-wayland-planner
WORKDIR /builder/gst-wayland-display
RUN --mount=type=cache,target=/builder/gst-wayland-display/target/ \
--mount=type=cache,target=/root/.cargo/bin/ \
RUN --mount=type=cache,target=/root/.cache/sccache \
--mount=type=cache,target=/tmp \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/builder/plugin/ \
--mount=type=cache,target=/usr/local/cargo/git/db \
--mount=type=cache,target=/usr/local/cargo/registry/ \
cargo cinstall --prefix=/builder/plugin/ \
&& cp -r /builder/plugin/ /artifacts/
export RUSTC_WRAPPER=/usr/local/bin/sccache && \
CARGO_HOME=/usr/local/cargo \
cargo chef prepare --recipe-path recipe.json
#******************************************************************************
# runtime
# gst-wayland-cacher
#******************************************************************************
FROM gst-wayland-builder AS gst-wayland-cacher
COPY --from=gst-wayland-planner /builder/gst-wayland-display/recipe.json .
RUN --mount=type=cache,target=/root/.cache/sccache \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/builder/target \
--mount=type=cache,target=/builder/plugin/ \
--mount=type=cache,target=/tmp \
export CARGO_TARGET_DIR=/builder/target \
export RUSTC_WRAPPER=/usr/local/bin/sccache && \
CARGO_HOME=/usr/local/cargo \
cargo chef cook --release --recipe-path recipe.json
#******************************************************************************
# gst-wayland-build
#******************************************************************************
FROM gst-wayland-builder AS gst-wayland-build
WORKDIR /builder/gst-wayland-display
COPY --from=gst-wayland-cacher /builder/ /builder/
COPY . .
RUN --mount=type=cache,target=/root/.cache/sccache \
--mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/builder/plugin/ \
--mount=type=cache,target=/builder/target \
export RUSTC_WRAPPER=/usr/local/bin/sccache \
export CARGO_TARGET_DIR=/builder/target \
export CARGO_BUILD_JOBS=$(nproc) \
export RUSTFLAGS="-C link-arg=-fuse-ld=mold" && \
CARGO_HOME=/usr/local/cargo \
cargo cinstall --prefix=/builder/plugin/ --release && \
cp -r /builder/plugin/ "$ARTIFACTS"
#******************************************************************************
# runtime
#******************************************************************************
FROM ${BASE_IMAGE} AS runtime
## Install Graphics, Media, and Audio packages ##
RUN sed -i '/#\[multilib\]/,/#Include = \/etc\/pacman.d\/mirrorlist/ s/#//' /etc/pacman.conf && \
sed -i "s/#Color/Color/" /etc/pacman.conf && \
pacman --noconfirm -Syu archlinux-keyring && \
dirmngr </dev/null > /dev/null 2>&1 && \
# Install mesa-git before Steam for simplicity
pacman --noconfirm -Sy mesa && \
# Install Steam
pacman --noconfirm -Sy steam steam-native-runtime && \
# Clean up pacman cache
paccache -rk1 && \
rm -rf /usr/share/info/* && \
rm -rf /usr/share/man/* && \
rm -rf /usr/share/doc/
# ## Install Graphics, Media, and Audio packages ##
# RUN sed -i '/#\[multilib\]/,/#Include = \/etc\/pacman.d\/mirrorlist/ s/#//' /etc/pacman.conf && \
# sed -i "s/#Color/Color/" /etc/pacman.conf && \
# pacman --noconfirm -Syu archlinux-keyring && \
# dirmngr </dev/null > /dev/null 2>&1 && \
# # Install mesa-git before Steam for simplicity
# pacman --noconfirm -Sy mesa && \
# # Install Steam
# pacman --noconfirm -Sy steam steam-native-runtime && \
# # Clean up pacman cache
# paccache -rk1 && \
# rm -rf /usr/share/info/* && \
# rm -rf /usr/share/man/* && \
# rm -rf /usr/share/doc/
RUN pacman -Sy --noconfirm --needed \
# Graphics packages
sudo xorg-xwayland labwc wlr-randr mangohud \
# GStreamer and plugins
gstreamer gst-plugins-base gst-plugins-good \
gst-plugins-bad gst-plugin-pipewire \
gst-plugin-rswebrtc gst-plugin-rsrtp \
# Audio packages
pipewire pipewire-pulse pipewire-alsa wireplumber \
# Non-latin fonts
noto-fonts-cjk \
# Other requirements
supervisor jq chwd lshw pacman-contrib && \
# Clean up pacman cache
paccache -rk1 && \
rm -rf /usr/share/info/* && \
rm -rf /usr/share/man/* && \
rm -rf /usr/share/doc/*
# RUN pacman -Sy --noconfirm --needed \
# # Graphics packages
# sudo xorg-xwayland labwc wlr-randr mangohud \
# # GStreamer and plugins
# gstreamer gst-plugins-base gst-plugins-good \
# gst-plugins-bad gst-plugin-pipewire \
# gst-plugin-rswebrtc gst-plugin-rsrtp \
# # Audio packages
# pipewire pipewire-pulse pipewire-alsa wireplumber \
# # Non-latin fonts
# noto-fonts-cjk \
# # Other requirements
# supervisor jq chwd lshw pacman-contrib && \
# # Clean up pacman cache
# paccache -rk1 && \
# rm -rf /usr/share/info/* && \
# rm -rf /usr/share/man/* && \
# rm -rf /usr/share/doc/*
#Install our backup manager
ARG LUDUSAVI_VERSION="0.28.0"
RUN pacman -Sy --noconfirm --needed wget &&\
wget "https://github.com/mtkennerly/ludusavi/releases/download/v${LUDUSAVI_VERSION}/ludusavi-v${LUDUSAVI_VERSION}-linux.tar.gz" -O "ludusavi.tar.gz" &&\
tar -xzvf ludusavi.tar.gz &&\
mv ludusavi /usr/bin/ &&\
#Clean up
rm *.tar.gz
# #Install our backup manager
# ARG LUDUSAVI_VERSION="0.28.0"
# RUN pacman -Sy --noconfirm --needed curl &&\
# curl -fsSL -o ludusavi.tar.gz "https://github.com/mtkennerly/ludusavi/releases/download/v${LUDUSAVI_VERSION}/ludusavi-v${LUDUSAVI_VERSION}-linux.tar.gz" &&\
# tar -xzvf ludusavi.tar.gz &&\
# mv ludusavi /usr/bin/ &&\
# #Clean up
# rm *.tar.gz
# Regenerate locale
RUN locale-gen
# # Regenerate locale
# RUN locale-gen
## User ##
# Create and setup user #
ENV USER="nestri" \
UID=1000 \
GID=1000 \
USER_PWD="nestri1234"
# ## User ##
# # Create and setup user #
# ENV USER="nestri" \
# UID=1000 \
# GID=1000 \
# USER_PWD="nestri1234"
RUN mkdir -p /home/${USER} && \
groupadd -g ${GID} ${USER} && \
useradd -d /home/${USER} -u ${UID} -g ${GID} -s /bin/bash ${USER} && \
chown -R ${USER}:${USER} /home/${USER} && \
echo "${USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \
echo "${USER}:${USER_PWD}" | chpasswd
# RUN mkdir -p /home/${USER} && \
# groupadd -g ${GID} ${USER} && \
# useradd -d /home/${USER} -u ${UID} -g ${GID} -s /bin/bash ${USER} && \
# chown -R ${USER}:${USER} /home/${USER} && \
# echo "${USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \
# echo "${USER}:${USER_PWD}" | chpasswd
# Run directory #
RUN mkdir -p /run/user/${UID} && \
chown ${USER}:${USER} /run/user/${UID}
# # Run directory #
# RUN mkdir -p /run/user/${UID} && \
# chown ${USER}:${USER} /run/user/${UID}
# Groups #
RUN usermod -aG input root && usermod -aG input ${USER} && \
usermod -aG video root && usermod -aG video ${USER} && \
usermod -aG render root && usermod -aG render ${USER} && \
usermod -aG seat root && usermod -aG seat ${USER}
# # Groups #
# RUN usermod -aG input root && usermod -aG input ${USER} && \
# usermod -aG video root && usermod -aG video ${USER} && \
# usermod -aG render root && usermod -aG render ${USER} && \
# usermod -aG seat root && usermod -aG seat ${USER}
## Copy files from builders ##
# this is done here at end to not trigger full rebuild on changes to builder
# nestri
COPY --from=gst-builder /artifacts/nestri-server /usr/bin/nestri-server
# ## Copy files from builders ##
# # this is done here at end to not trigger full rebuild on changes to builder
# # nestri
# COPY --from=nestri-server-build /artifacts/nestri-server /usr/bin/nestri-server
# gstwayland
COPY --from=gstwayland-builder /artifacts/plugin/include/libgstwaylanddisplay /usr/include/
COPY --from=gstwayland-builder /artifacts/plugin/lib/*libgstwayland* /usr/lib/
COPY --from=gstwayland-builder /artifacts/plugin/lib/gstreamer-1.0/libgstwayland* /usr/lib/gstreamer-1.0/
COPY --from=gstwayland-builder /artifacts/plugin/lib/pkgconfig/gstwayland* /usr/lib/pkgconfig/
COPY --from=gstwayland-builder /artifacts/plugin/lib/pkgconfig/libgstwayland* /usr/lib/pkgconfig/
COPY --from=gst-wayland-build /artifacts/plugin/include/libgstwaylanddisplay /usr/include/
COPY --from=gst-wayland-build /artifacts/plugin/lib/*libgstwayland* /usr/lib/
COPY --from=gst-wayland-build /artifacts/plugin/lib/gstreamer-1.0/libgstwayland* /usr/lib/gstreamer-1.0/
COPY --from=gst-wayland-build /artifacts/plugin/lib/pkgconfig/gstwayland* /usr/lib/pkgconfig/
COPY --from=gst-wayland-build /artifacts/plugin/lib/pkgconfig/libgstwayland* /usr/lib/pkgconfig/
## Copy scripts ##
COPY packages/scripts/ /etc/nestri/
# Set scripts as executable #
RUN chmod +x /etc/nestri/envs.sh /etc/nestri/entrypoint.sh /etc/nestri/entrypoint_nestri.sh
# ## Copy scripts ##
# COPY packages/scripts/ /etc/nestri/
# # Set scripts as executable #
# RUN chmod +x /etc/nestri/envs.sh /etc/nestri/entrypoint.sh /etc/nestri/entrypoint_nestri.sh
## Set runtime envs ##
ENV XDG_RUNTIME_DIR=/run/user/${UID} \
HOME=/home/${USER}
# ## Set runtime envs ##
# ENV XDG_RUNTIME_DIR=/run/user/${UID} \
# HOME=/home/${USER}
# Required for NVIDIA.. they want to be special like that #
ENV NVIDIA_DRIVER_CAPABILITIES=all
ENV NVIDIA_VISIBLE_DEVICES=all
# # Required for NVIDIA.. they want to be special like that #
# ENV NVIDIA_DRIVER_CAPABILITIES=all
# ENV NVIDIA_VISIBLE_DEVICES=all
# DBus run directory creation #
RUN mkdir -p /run/dbus
# # DBus run directory creation #
# RUN mkdir -p /run/dbus
# Wireplumber disable suspend #
# Remove suspend node
RUN sed -z -i 's/{[[:space:]]*name = node\/suspend-node\.lua,[[:space:]]*type = script\/lua[[:space:]]*provides = hooks\.node\.suspend[[:space:]]*}[[:space:]]*//g' /usr/share/wireplumber/wireplumber.conf
# Remove "hooks.node.suspend" want
RUN sed -i '/wants = \[/{s/hooks\.node\.suspend\s*//; s/,\s*\]/]/}' /usr/share/wireplumber/wireplumber.conf
# # Wireplumber disable suspend #
# # Remove suspend node
# RUN sed -z -i 's/{[[:space:]]*name = node\/suspend-node\.lua,[[:space:]]*type = script\/lua[[:space:]]*provides = hooks\.node\.suspend[[:space:]]*}[[:space:]]*//g' /usr/share/wireplumber/wireplumber.conf
# # Remove "hooks.node.suspend" want
# RUN sed -i '/wants = \[/{s/hooks\.node\.suspend\s*//; s/,\s*\]/]/}' /usr/share/wireplumber/wireplumber.conf
ENTRYPOINT ["supervisord", "-c", "/etc/nestri/supervisord.conf"]
# ENTRYPOINT ["supervisord", "-c", "/etc/nestri/supervisord.conf"]

View File

@@ -13,7 +13,6 @@
"@pulumi/pulumi": "^3.134.0",
"@types/aws-lambda": "8.10.145",
"prettier": "^3.2.5",
"turbo": "^2.0.12",
"typescript": "^5.4.5"
},
"engines": {

View File

@@ -1,16 +0,0 @@
[package]
name = "dev"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "nestri-test-server"
path = "src/main.rs"
[dependencies]
webrtc = "0.11.0"
tokio = { version = "1.41.1", features = ["full"] }
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1.0.215", features = ["derive"]}
serde_json = "1.0.133"

View File

@@ -1,11 +0,0 @@
FROM archlinux:latest
RUN pacman -Syu --noconfirm
RUN pacman -Su --noconfirm \
gstreamer gst-plugins-base gst-plugins-good gst-plugin-rswebrtc
RUN pacman -Syu --noconfirm \
mesa mesa-utils xorg-xwayland vulkan-intel vpl-gpu-rt intel-media-driver gst-plugin-va gst-plugins-bad gst-plugin-fmp4 gst-plugin-qsv gst-plugin-pipewire
CMD [ "bash","-c", "gst-launch-1.0 videotestsrc ! openh264enc ! whip0. audiotestsrc ! opusenc ! whip0. whipclientsink name=whip0 signaller::whip-endpoint=http://localhost:8088/api/whip/test" ]

View File

@@ -1,17 +0,0 @@
#! /bin/bash -e
# sudo apt install build-essential -y
# To run tests, run the relay first - go run main.go.
# Run the docker container next - docker run --rm --init -d --device /dev/dri --network=host test-server
# Then run the nestri-test-server - cd packages/relay/dev cargo run
# Then run the frontend site, and navigate to http://localhost:5713/play/test
# Expected behavior, see some random messages on the browser's console tab
# And if you input works correctly, it should be logged to the console on the server-side of things
# docker build -t test-server -f Containerfile .
# docker run --rm --init -d --device /dev/dri --network=host test-server
# echo -e "Navigate to http://localhost:5713/play/test"

View File

@@ -1,11 +0,0 @@
mod room;
#[tokio::main]
async fn main() -> std::io::Result<()> {
let room = "test";
let base_url = "http://localhost:8088";
let mut room_handler = room::Room::new(room, base_url).await?;
room_handler.run().await?;
Ok(())
}

View File

@@ -1,292 +0,0 @@
use reqwest;
use std::collections::HashSet;
use std::io;
use std::sync::Arc;
use tokio::sync::mpsc;
use tokio::sync::Mutex;
use tokio::time::Duration;
use webrtc::api::interceptor_registry::register_default_interceptors;
// use std::collections::HashSet;
use serde::{Deserialize, Serialize};
use serde_json::from_str;
use webrtc::api::media_engine::MediaEngine;
use webrtc::api::APIBuilder;
use webrtc::data_channel::data_channel_message::DataChannelMessage;
use webrtc::ice_transport::ice_server::RTCIceServer;
use webrtc::interceptor::registry::Registry;
use webrtc::peer_connection::configuration::RTCConfiguration;
use webrtc::peer_connection::math_rand_alpha;
use webrtc::peer_connection::peer_connection_state::RTCPeerConnectionState;
use webrtc::peer_connection::sdp::session_description::RTCSessionDescription;
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
enum InputMessage {
#[serde(rename = "mousemove")]
MouseMove { x: i32, y: i32 },
#[serde(rename = "mousemoveabs")]
MouseMoveAbs { x: i32, y: i32 },
#[serde(rename = "wheel")]
Wheel { x: f64, y: f64 },
#[serde(rename = "mousedown")]
MouseDown { key: i32 },
// Add other variants as needed
#[serde(rename = "mouseup")]
MouseUp { key: i32 },
#[serde(rename = "keydown")]
KeyDown { key: i32 },
#[serde(rename = "keyup")]
KeyUp { key: i32 },
}
pub struct Room {
peer_connection: Arc<webrtc::peer_connection::RTCPeerConnection>,
data_channel: Arc<webrtc::data_channel::RTCDataChannel>,
done_tx: mpsc::Sender<()>,
done_rx: mpsc::Receiver<()>,
base_url: String,
stream_name: String,
// pipeline: Arc<Mutex<gst::Pipeline>>,
}
impl Room {
pub async fn new(
stream_name: &str,
base_url: &str,
// pipeline: Arc<Mutex<gst::Pipeline>>,
) -> io::Result<Self> {
// Create a MediaEngine object to configure the supported codec
let mut m = MediaEngine::default();
// Register default codecs
let _ = m.register_default_codecs().map_err(map_to_io_error)?;
let mut registry = Registry::new();
// Use the default set of Interceptors
registry = register_default_interceptors(registry, &mut m).map_err(map_to_io_error)?;
// Create the API object with the MediaEngine
let api = APIBuilder::new()
.with_media_engine(m)
.with_interceptor_registry(registry)
.build();
// Prepare the configuration
let config = RTCConfiguration {
ice_servers: vec![RTCIceServer {
urls: vec!["stun:stun.l.google.com:19302".to_owned()],
..Default::default()
}],
..Default::default()
};
// Create a new RTCPeerConnection
let peer_connection = Arc::new(
api.new_peer_connection(config)
.await
.map_err(map_to_io_error)?,
);
// Create a datachannel with label 'data'
let data_channel = peer_connection
.create_data_channel("input", None)
.await
.map_err(map_to_io_error)?;
let (done_tx, done_rx) = mpsc::channel::<()>(1);
let done_tx_clone = done_tx.clone();
// Peer connection state change handler
peer_connection.on_peer_connection_state_change(Box::new(
move |s: RTCPeerConnectionState| {
println!("Peer Connection State has changed: {s}");
if s == RTCPeerConnectionState::Failed {
println!("Peer Connection has gone to failed exiting");
let _ = done_tx_clone.try_send(());
}
Box::pin(async {})
},
));
Ok(Self {
peer_connection,
// pipeline,
data_channel,
done_tx,
done_rx,
base_url: base_url.to_string(),
stream_name: stream_name.to_string(),
})
}
pub async fn run(&mut self) -> io::Result<()> {
// Create an async channel for sending events to the pipeline
let (event_tx, mut event_rx) = mpsc::channel(10);
// A shared state to track currently pressed keys
let pressed_keys = Arc::new(tokio::sync::Mutex::new(HashSet::new()));
// Spawn a task to process events for the pipeline
let pipeline_task = {
// let pipeline = Arc::clone(self.pipeline);
// let pipeline_clone = self.pipeline.clone();
tokio::spawn(async move {
while let Some(event) = event_rx.recv().await {
// let pipeline = pipeline_clone.lock().await;
// pipeline.send_event(event);
println!("Invoked an event: {}", event)
}
})
};
let data_channel = self.data_channel.clone();
//TODO: Handle heartbeats here
let d1 = Arc::clone(&self.data_channel);
data_channel.on_open(Box::new(move || {
println!("Data channel '{}'-'{}' open. Random messages will now be sent to any connected DataChannels every 5 seconds", d1.label(), d1.id());
let d2 = Arc::clone(&d1);
Box::pin(async move {
let mut result = std::io::Result::<usize>::Ok(0);
while result.is_ok() {
let timeout = tokio::time::sleep(Duration::from_secs(5));
tokio::pin!(timeout);
tokio::select! {
_ = timeout.as_mut() =>{
let message = math_rand_alpha(15);
println!("Sending '{message}'");
result = d2.send_text(message).await.map_err(map_to_io_error);
}
};
}
})
}));
// Data channel message handler
let d_label = data_channel.label().to_owned();
data_channel.on_message(Box::new(move |msg: DataChannelMessage| {
let msg_str = String::from_utf8(msg.data.to_vec()).unwrap();
println!("Message from DataChannel '{d_label}': '{msg_str}'");
let event_tx = event_tx.clone();
let pressed_keys = Arc::clone(&pressed_keys);
tokio::spawn(async move {
if let Ok(input_msg) = from_str::<InputMessage>(&msg_str) {
if let Some(event) = handle_input_message(input_msg, &pressed_keys).await {
event_tx.send(event).await.unwrap();
}
}
});
Box::pin(async {})
}));
// Create an offer to send to the browser
let offer = self
.peer_connection
.create_offer(None)
.await
.map_err(map_to_io_error)?;
// Create channel that is blocked until ICE Gathering is complete
let mut gather_complete = self.peer_connection.gathering_complete_promise().await;
// Sets the LocalDescription, and starts our UDP listeners
self.peer_connection
.set_local_description(offer)
.await
.map_err(map_to_io_error)?;
// Block until ICE Gathering is complete, disabling trickle ICE
// we do this because we only can exchange one signaling message
// in a production application you should exchange ICE Candidates via OnICECandidate
let _ = gather_complete.recv().await;
if let Some(local_description) = self.peer_connection.local_description().await {
let url = format!("{}/api/whep/{}", self.base_url, self.stream_name);
let response = reqwest::Client::new()
.post(&url)
.header("Content-Type", "application/sdp")
.body(local_description.sdp.clone()) // clone if you don't want to move offer.sdp
.send()
.await
.map_err(map_to_io_error)?;
let answer = response
.json::<RTCSessionDescription>()
.await
.map_err(map_to_io_error)?;
self.peer_connection
.set_remote_description(answer)
.await
.map_err(map_to_io_error)?;
} else {
println!("generate local_description failed!");
};
println!("Press ctrl-c to stop");
tokio::select! {
_ = self.done_rx.recv() => {
println!("received done signal!");
}
_ = tokio::signal::ctrl_c() => {
println!();
}
};
self.peer_connection
.close()
.await
.map_err(map_to_io_error)?;
//FIXME: Ctr + C is not working... i suspect it has something to do with this guy -- Do not forget to fix packages/server/room.rs as well
pipeline_task.await?;
Ok(())
}
}
fn map_to_io_error<E: std::fmt::Display>(e: E) -> io::Error {
io::Error::new(io::ErrorKind::Other, format!("{}", e))
}
async fn handle_input_message(
input_msg: InputMessage,
pressed_keys: &Arc<tokio::sync::Mutex<HashSet<i32>>>,
) -> Option<String> {
match input_msg {
InputMessage::MouseMove { x, y } => Some("MouseMoved".to_string()),
InputMessage::MouseMoveAbs { x, y } => Some("MouseMoveAbsolute".to_string()),
InputMessage::KeyDown { key } => {
let mut keys = pressed_keys.lock().await;
// If the key is already pressed, return to prevent key lockup
if keys.contains(&key) {
return None;
}
keys.insert(key);
Some("KeyDown".to_string())
}
InputMessage::KeyUp { key } => {
let mut keys = pressed_keys.lock().await;
// Remove the key from the pressed state when released
keys.remove(&key);
Some("KeyUp".to_string())
}
InputMessage::Wheel { x, y } => Some("Wheel".to_string()),
InputMessage::MouseDown { key } => Some("MouseDown".to_string()),
InputMessage::MouseUp { key } => Some("MouseUp".to_string()),
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "nestri-server"
version = "0.1.0"
version = "0.1.0-alpha.2"
edition = "2021"
[[bin]]
@@ -8,9 +8,9 @@ name = "nestri-server"
path = "src/main.rs"
[dependencies]
gst.workspace = true
gst-webrtc.workspace = true
gstrswebrtc.workspace = true
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main", features = ["v1_24"] }
gst-webrtc = { package = "gstreamer-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main", features = ["v1_24"] }
gstrswebrtc = { package = "gst-plugin-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs", branch = "main", features = ["v1_22"] }
serde = {version = "1.0.214", features = ["derive"] }
tokio = { version = "1.41.0", features = ["full"] }
clap = { version = "4.5.20", features = ["env"] }

View File

@@ -1 +0,0 @@
{}

View File

@@ -1,42 +0,0 @@
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"ui": "tui",
"tasks": {
"build": {
"dependsOn": [
"^build"
],
"inputs": [
"$TURBO_DEFAULT$",
".env*"
],
"outputs": [
".next/**",
"!.next/cache/**",
"dist/**"
]
},
"web#build": {
"dependsOn": [
"^build"
],
"outputs": [
"dist/**"
]
},
"lint": {
"dependsOn": [
"^lint",
"^build"
]
},
"dev": {
"cache": false,
"persistent": true
},
"clean": {
"cache": false
}
}
}