Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small features and more tests #18

Merged
merged 18 commits into from
Aug 1, 2024
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
108 changes: 108 additions & 0 deletions .github/workflows/test-unit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Build and Test

on:
push:
branches:
- "master"
pull_request:
branches:
- master

jobs:
unit-test:
name: Unit tests
runs-on: ubuntu-24.04

steps:
- name: Checkout repository
uses: actions/checkout@v4

- uses: nixbuild/nix-quick-install-action@v27

- uses: nix-community/cache-nix-action@v5
with:
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
restore-prefixes-first-match: nix-${{ runner.os }}-

- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package*.json') }}
restore-keys: |
${{ runner.os }}-npm-

- run: nix develop -c npm i

- name: Run unit tests
run: nix develop -c task test-unit

build:
name: Build
runs-on: ubuntu-24.04

steps:
- name: Checkout repository
uses: actions/checkout@v4

- uses: nixbuild/nix-quick-install-action@v27

- uses: nix-community/cache-nix-action@v5
with:
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix') }}
restore-prefixes-first-match: nix-${{ runner.os }}-

- name: Build container image
run: nix develop -c task build

- name: Smoke test container image
run: nix develop -c task build-smoke-test

test-install:
name: Test install/run
runs-on: "${{ matrix.os }}"
strategy:
matrix:
os:
- ubuntu-24.04
- ubuntu-22.04
- ubuntu-20.04
# - macos-14 # Docker install suported yet
- macos-13
- macos-12

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: (MacOS) Setup Docker
if: startsWith(matrix.os, 'macos')
# uses: docker-practice/actions-setup-docker@master
# timeout-minutes: 12
# uses: douglascamata/setup-docker-macos-action@main
uses: crazy-max/ghaction-setup-docker@v3

# Test the install script and cloudypad.sh for current commit
- name: Run install script
run: |
curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/${{ github.sha }}/install.sh | CLOUDYPAD_VERSION=${{ github.sha }} sh

- name: Check version
run: |
export PATH=$PATH:$HOME/.cloudypad/bin
export CLOUDYPAD_CONTAINER_NO_TTY=true
export CLOUDYPAD_CLI_LAUNCHER_DEBUG=true
cloudypad --version

test-install-container:
name: Test install/run in containers
runs-on: ubuntu-22.04

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Test install.sh script
run: test/shell/test-install.sh

- name: Test cloudypad.sh script
run: test/shell/test-cloudypad.sh
1 change: 0 additions & 1 deletion .mocharc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"extension": ["ts"],
"spec": "test/**.spec.ts",
"require": "ts-node/register"
}
59 changes: 56 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,24 @@ Your own gaming gear in the Cloud ! 🎮 ⛅
- [Detailed setup per Clouder](#detailed-setup-per-clouder)
- [Paperspace](#paperspace)
- [AWS](#aws)
- [Quotas](#quotas)
- [Profile and environment variables](#profile-and-environment-variables)
- [FAQ](#faq)
- [How much will I pay ? 🫰](#how-much-will-i-pay--)
- [Paperspace](#paperspace-1)
- [AWS](#aws-1)
- [What are the recommended GPU and specs for my instance ?](#what-are-the-recommended-gpu-and-specs-for-my-instance-)
- [AWS](#aws-2)
- [Paperspace](#paperspace-2)
- [How can I log-in to Steam?](#how-can-i-log-in-to-steam)
- [How to play game on Steam / Why does my Steam game doesn't launch ?](#how-to-play-game-on-steam--why-does-my-steam-game-doesnt-launch-)
- [Using Steam, why does my game take forever to "cache Vulkan shader" ?](#using-steam-why-does-my-game-take-forever-to-cache-vulkan-shader-)
- [I have a black screen when I connect to my instance](#i-have-a-black-screen-when-i-connect-to-my-instance)
- [I Found an bug or I have a suggestion](#i-found-an-bug-or-i-have-a-suggestion)
- [How does all of this work?](#how-does-all-of-this-work)
- [Will Cloudy Pad become a paid product ?](#will-cloudy-pad-become-a-paid-product-)
- [Known issues](#known-issues)
- [Docker for MacOS and VirtioFS](#docker-for-macos-and-virtiofs)
- [License](#license)

## What is Cloudy Pad ?
Expand Down Expand Up @@ -67,7 +72,7 @@ Not familiar with terms like _"Cloud gaming"_, _"Moonlight"_, _"Cloud Provider"_

Cloudy Pad deploys a Cloud gaming gear using a Cloud provider of your choice:
- 💸 While Cloudy Pad itself is free and open-source, charges may incur for Cloud provider usage. Make sure you [understand the costs](#how-much-will-i-pay--)
- Cloudy Pad lets you play on Linux. Using Steam may require [Proton](https://www.protondb.com/). You can check your game compatibility on [Proton website](https://www.protondb.com/) or see [how to play games on Steam](#how-to-play-game-on-steam--why-does-my-steam-game-doesnt-launch-).
- Cloudy Pad lets you play on Linux. Using Steam may require [Proton](https://github.com/ValveSoftware/Proton). You can check your game compatibility on [ProtonDB website](https://www.protondb.com/) or see [how to play games on Steam](#how-to-play-game-on-steam--why-does-my-steam-game-doesnt-launch-).

Prerequisites:
- A Cloud provider account, one of:
Expand All @@ -76,12 +81,18 @@ Prerequisites:
- [Moonlight](https://moonlight-stream.org/) streaming client
- [Docker](https://docs.docker.com/engine/install/) (other container engine support will come soon)

Install `cloudypad` CLI:
Install latest version of `cloudypad` CLI:

```sh
curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | sh
```

Living on the edge? Install directly from a branch or a Git commit:

```sh
curl -fsSL https://raw.githubusercontent.com/PierreBeucher/cloudypad/master/install.sh | CLOUDYPAD_VERSION="master" sh
```

You may need to setup a few things on your Cloud provider (eg. API key or SSH key). Checkout [per-Clouder setup specifities](#detailed-setup-per-clouder).

Once ready, create your instance with `cloudypad` CLI:
Expand Down Expand Up @@ -251,6 +262,26 @@ You're good to go ! Create your instance with
cloudypad create
```

#### Quotas

You may need to increase quota to create the related instance type. If you get an error related to quota:
- Go to AWS console and open "Service Quotas" service
- Go to _AWS Services_ > search for _Amazon Elastic Compute Cloud (Amazon EC2)_ and open it
- Search for _Running On-Demand G and VT instances_ (or the related instance type) and request a quota increase
- Use a quota value according to the instance type you want to use. For example, `2xlarge` requires at least 8 vCPU.

See [AWS service quotas](https://docs.aws.amazon.com/general/latest/gr/aws_service_limits.html) for details.

#### Profile and environment variables

If you want to use an AWS Profile or specific AWS credentials, use environment variables such as:

```sh
export AWS_PROFILE=myprofile
```

See [AWS environment variable list](https://docs.aws.amazon.com/sdkref/latest/guide/settings-reference.html#EVarSettings) for existing variables. Not there are certain limitations as most Cloudy Pad workflow run in a container which may cause some variables to misbehave. Please create an issue if you encounter a problem.

## FAQ

### How much will I pay ? 🫰
Expand Down Expand Up @@ -304,14 +335,21 @@ Paperspace `RTX4000` or `P4000` or `M4000` are relatively cheap and powerful eno

Use higher-tier instance if you have latency related to resource consumption.

### How can I log-in to Steam?

When you run Steam, you'll be prompted to login either via QR code or login/password. You can either:

- Enter your login / password manually
- Use the Steam app to login via QR code: download and login with the Steam app on your smartphone, then click on the Steam Guard icon (shield icon at the bottom) and scan the QR code shown.

### How to play game on Steam / Why does my Steam game doesn't launch ?

In order to play games on Steam you may need to enable Proton:

- Go to game properties (_Gear button on the right > Properties_)
- Enable Proton in the Compatibility menu

It's recommended to check your game Proton compatibility [ProtonDB](https://www.protondb.com/). You may need to add a few Launch options (_Game properties > General > Launch options_).
It's recommended to check your game Proton compatibility on [ProtonDB](https://www.protondb.com/). You may need to add a few Launch options (_Game properties > General > Launch options_).

### Using Steam, why does my game take forever to "cache Vulkan shader" ?

Expand Down Expand Up @@ -341,6 +379,21 @@ Probably not in its current form. Considering I'm really _not_ happy about the [

Cloudy Pad may have a Premium or Pro offer in the future, but for a personal simple use it will remain FOSS.

## Known issues

### Docker for MacOS and VirtioFS

For MacOS, if your Docker installation use VirtioFS, Cloudy Pad may fail with a Docker-related error such as:

```
Error response from daemon: error while creating mount source path '/private/tmp/com.apple.launchd.ABCDEF/Listeners': mkdir /private/tmp/com.apple.launchd.ABCDEF/Listeners: operation not supported
```

This is a bug when using Docker for Mac VirtioFS file sharing with SSH agent. The bug is still being worked on, as a workaround you can either:

- Disable SSH agent before running Cloudy Pad, eg. `unset SSH_AUTH_SOCK`
- Switch Docker for Mac config to non-VirtioFS (eg. gRPC FUSE): go to _config > Resources > File Sharing_ and update config.

## License

[GNU GENERAL PUBLIC LICENSE](./LICENSE.txt)
25 changes: 24 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,27 @@ tasks:
cmd: >-
CLOUDYPAD_IMAGE="cloudypad:local" ./cloudypad.sh {{.CLI_ARGS}}

#
# Build and test
#

test-unit:
cmd: npx mocha ./test/unit/**/*.spec.ts

build:
cmd: docker build . -t cloudypad:local
cmd: docker build -t cloudypad:local .

build-smoke-test:
cmds:
- task: build
- docker run cloudypad:local --version
- >-
[ $(docker run cloudypad:local --version) = $(cat package.json | jq .version -r) ]
|| echo "Version in container image doesn't match package.json version"

#
# Utils
#

# Generate Typescript Paperspace client from OpenAPI specifications
paperspace-client-gen:
Expand Down Expand Up @@ -47,6 +66,10 @@ tasks:
-g typescript-axios
-o {{ .PAPERSPACE_GENERATED_DIR }}

#
# Release
#

release-prepare:
cmd: ./hack/prepare-release.sh

Expand Down
50 changes: 39 additions & 11 deletions cloudypad.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
# and run instructions.
# Only a few commands need to run directly for user (eg. moonlight setup)

if [ -n "$CLOUDYPAD_CLI_LAUNCHER_DEBUG" ]; then
set -x
fi

CLOUDYPAD_VERSION=0.1.1
CLOUDYPAD_IMAGE="${CLOUDYPAD_IMAGE:-"crafteo/cloudypad:$CLOUDYPAD_VERSION"}"
CLOUDYPAD_TARGET_IMAGE="crafteo/cloudypad-local-runner:local"
Expand All @@ -32,27 +36,30 @@ HOST_GROUP_NAME=$(id -gn)
cat <<EOF > /tmp/Dockerfile-cloudypad-run
FROM $CLOUDYPAD_IMAGE

# Ensure the host user matches user in container:
# Ensure the host user matches user in container.
# If host user is root, do nothing and run container as root, otherwise:
# - Delete user matching host's user ID if already exists
# - Create group if not exists
# - Create user
RUN if id -u $HOST_UID >/dev/null 2>&1; then \
deluser \$(id -un $HOST_UID); \
fi && \
if ! getent group $HOST_GID >/dev/null; then \
groupadd -g $HOST_GID $HOST_GROUP_NAME; \
fi && \
useradd -u $HOST_UID -g $HOST_GID --home-dir $HOME --create-home $HOST_USER_NAME
RUN if [ "$(id -u $HOST_UID)" -ne 0 ] >/dev/null 2>&1; then \
if id -u $HOST_UID >/dev/null 2>&1; then \
deluser \$(id -un $HOST_UID); \
fi && \
if ! getent group $HOST_GID >/dev/null; then \
groupadd -g $HOST_GID $HOST_GROUP_NAME; \
fi && \
useradd -u $HOST_UID -g $HOST_GID --home-dir $HOME --create-home $HOST_USER_NAME; \
fi

USER $HOST_UID
EOF

container_build_output=$(docker build --progress plain -t $CLOUDYPAD_TARGET_IMAGE - < /tmp/Dockerfile-cloudypad-run 2>&1)
container_build_output=$(docker build -t $CLOUDYPAD_TARGET_IMAGE - < /tmp/Dockerfile-cloudypad-run 2>&1)
container_build_result=$?

if [ $container_build_result -ne 0 ]; then
echo "Error: could not build CloudyPad container image, build exited with code: $container_build_result" >&2
echo "Build command was: docker build --progress plain -t $CLOUDYPAD_TARGET_IMAGE - < /tmp/Dockerfile-cloudypad-run 2>&1" >&2
echo "Build command was: docker build -t $CLOUDYPAD_TARGET_IMAGE - < /tmp/Dockerfile-cloudypad-run 2>&1" >&2
echo "Build output: "
echo "$container_build_output"
echo
Expand Down Expand Up @@ -80,7 +87,15 @@ run_cloudypad_docker() {
)

# Build run command with proper directories
local cmd="docker run --rm -it"
local cmd="docker run --rm"

# Set interactive+tty by default
# no tty if CLOUDYPAD_CONTAINER_NO_TTY is set (for CI)
if [ -n "$CLOUDYPAD_CONTAINER_NO_TTY" ]; then
cmd="$cmd -t"
else
cmd="$cmd -it"
fi

# Only mount a directory if it exists on host
for mount in "${mounts[@]}"; do
Expand All @@ -89,6 +104,19 @@ run_cloudypad_docker() {
fi
done

# Local environment variables to pass-through in container
local env_vars=(
"AWS_PROFILE" "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" "AWS_SESSION_TOKEN"
"AWS_DEFAULT_REGION" "AWS_REGION" "AWS_ENDPOINT_URL" "AWS_PROFILE"
"AWS_ROLE_ARN" "AWS_ROLE_SESSION_NAME"
)

for env_var in "${env_vars[@]}"; do
if [ -n "${!env_var}" ]; then
cmd+=" -e $env_var=${!env_var}"
fi
done

# Add SSH agent volume and env var if it's available locally
if [ -n "$SSH_AUTH_SOCK" ]; then
cmd+=" -v $SSH_AUTH_SOCK:/ssh-agent -e SSH_AUTH_SOCK=/ssh-agent"
Expand Down
22 changes: 22 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// @ts-check

import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
// import foo from '@typescript-eslint/parser'
import * as eslintImport from 'eslint-plugin-import'

export default tseslint.config(
{
ignores: ["dist/", "tmp/"],
},
eslint.configs.recommended,
...tseslint.configs.recommended,
{
plugins: {
'import': eslintImport
},
rules: {
'import/no-cycle': ['warn', { maxDepth: Infinity }]
},
},
)
Loading
Loading