mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-21 17:53:45 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5ad2bfdb75 | |||
| b742557d62 | |||
| 5ecd05aedf | |||
| 5ae100aa1d | |||
| be76a88b80 | |||
| 049d225d5a |
@@ -103,20 +103,25 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
cd ${{ github.workspace }}/openpilot/openpilot
|
cd ${{ github.workspace }}/openpilot/openpilot
|
||||||
if [ "${{ inputs.target_hardware }}" != "usbgpu" ]; then
|
if [ "${{ inputs.target_hardware }}" != "usbgpu" ]; then
|
||||||
git lfs pull -X "selfdrive/modeld/models/big_*.onnx" -X "selfdrive/modeld/models/dmonitoring_*.onnx"
|
git lfs pull -X "**/selfdrive/modeld/models/big_*.onnx,**/selfdrive/modeld/models/dmonitoring_*.onnx"
|
||||||
rm -f selfdrive/modeld/models/big_*.onnx selfdrive/modeld/models/dmonitoring_*.onnx
|
rm -f selfdrive/modeld/models/big_*.onnx selfdrive/modeld/models/dmonitoring_*.onnx
|
||||||
else
|
else
|
||||||
git lfs pull -I "selfdrive/modeld/models/big_*.onnx"
|
git lfs pull -I "**/selfdrive/modeld/models/big_*.onnx" -X ""
|
||||||
find selfdrive/modeld/models -name "*.onnx" ! -name "big_*.onnx" -delete
|
find selfdrive/modeld/models -name "*.onnx" ! -name "big_*.onnx" -delete
|
||||||
fi
|
fi
|
||||||
|
if grep -lIF "version https://git-lfs.github.com/spec/v1" selfdrive/modeld/models/*.onnx; then
|
||||||
|
echo "::error::the ONNX files above are still LFS pointers, not real models"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
- name: 'Upload Artifact'
|
- name: 'Upload Artifact'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: models-${{ env.REF }}${{ inputs.artifact_suffix }}
|
name: models-${{ env.REF }}${{ inputs.artifact_suffix }}
|
||||||
path: ${{ github.workspace }}/openpilot/openpilot/selfdrive/modeld/models/*.onnx
|
path: ${{ github.workspace }}/openpilot/openpilot/selfdrive/modeld/models/*.onnx
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
build_model:
|
build_model:
|
||||||
runs-on: [self-hosted, tici]
|
runs-on: [self-hosted, usbgpu]
|
||||||
needs: get_model
|
needs: get_model
|
||||||
env:
|
env:
|
||||||
MODEL_NAME: ${{ inputs.custom_name || inputs.upstream_branch }} (${{ needs.get_model.outputs.model_date }})
|
MODEL_NAME: ${{ inputs.custom_name || inputs.upstream_branch }} (${{ needs.get_model.outputs.model_date }})
|
||||||
@@ -127,7 +132,6 @@ jobs:
|
|||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- run: git lfs pull
|
|
||||||
|
|
||||||
- name: Set environment variables
|
- name: Set environment variables
|
||||||
id: set-env
|
id: set-env
|
||||||
@@ -160,7 +164,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
source ${UV_PROJECT_ENVIRONMENT}/bin/activate
|
source ${UV_PROJECT_ENVIRONMENT}/bin/activate
|
||||||
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/ ${{ github.workspace }}/scripts/manage-powersave.py --disable
|
||||||
rm -rf ${{ env.MODELS_DIR }}/*.onnx
|
rm -rf ${{ env.MODELS_DIR }}/*.onnx*
|
||||||
|
|
||||||
- name: Download model artifacts
|
- name: Download model artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@@ -180,6 +184,7 @@ jobs:
|
|||||||
MODEL_SIZE=$(python3 -c "from openpilot.common.transformations.model import MEDMODEL_INPUT_SIZE as s; print(f'{s[0]}x{s[1]}')")
|
MODEL_SIZE=$(python3 -c "from openpilot.common.transformations.model import MEDMODEL_INPUT_SIZE as s; print(f'{s[0]}x{s[1]}')")
|
||||||
CAMERA_RES=$(python3 -c "from openpilot.common.transformations.camera import _ar_ox_fisheye as a, _os_fisheye as o; print(f'{a.width}x{a.height} {o.width}x{o.height}')")
|
CAMERA_RES=$(python3 -c "from openpilot.common.transformations.camera import _ar_ox_fisheye as a, _os_fisheye as o; print(f'{a.width}x{a.height} {o.width}x{o.height}')")
|
||||||
|
|
||||||
|
TG_FLAGS_QCOM="DEV=QCOM IMAGE=1 FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 OPENPILOT_HACKS=1"
|
||||||
if [ "${{ inputs.target_hardware }}" == "usbgpu" ]; then
|
if [ "${{ inputs.target_hardware }}" == "usbgpu" ]; then
|
||||||
echo "USBGPU build"
|
echo "USBGPU build"
|
||||||
export USBGPU=1
|
export USBGPU=1
|
||||||
@@ -187,27 +192,40 @@ jobs:
|
|||||||
OUTPUT_PKL="${{ env.MODELS_DIR }}/big_driving_tinygrad.pkl"
|
OUTPUT_PKL="${{ env.MODELS_DIR }}/big_driving_tinygrad.pkl"
|
||||||
else
|
else
|
||||||
echo "QCOM build"
|
echo "QCOM build"
|
||||||
TG_FLAGS="DEV=QCOM IMAGE=1 FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 OPENPILOT_HACKS=1"
|
TG_FLAGS="$TG_FLAGS_QCOM"
|
||||||
OUTPUT_PKL="${{ env.MODELS_DIR }}/driving_tinygrad.pkl"
|
OUTPUT_PKL="${{ env.MODELS_DIR }}/driving_tinygrad.pkl"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate metadata for all ONNX files
|
# Generate metadata for all ONNX files
|
||||||
find "${{ env.MODELS_DIR }}" -maxdepth 1 -name '*.onnx' | while IFS= read -r onnx_file; do
|
find "${{ env.MODELS_DIR }}" -maxdepth 1 -name '*.onnx' | while IFS= read -r onnx_file; do
|
||||||
echo "Generating metadata: $onnx_file"
|
echo "Generating metadata: $onnx_file"
|
||||||
env ${TG_FLAGS} python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true
|
env ${TG_FLAGS_QCOM} python3 "${{ env.MODELS_DIR }}/../get_model_metadata.py" "$onnx_file" || true
|
||||||
done
|
done
|
||||||
|
|
||||||
# Detect model type and build compile args
|
# Detect model type and build compile args
|
||||||
VISION_ONNX="${{ env.MODELS_DIR }}/driving_vision.onnx"
|
VISION_ONNX=""
|
||||||
POLICY_ONNX="${{ env.MODELS_DIR }}/driving_policy.onnx"
|
for f in "${{ env.MODELS_DIR }}/driving_vision.onnx" "${{ env.MODELS_DIR }}/big_driving_vision.onnx"; do
|
||||||
OFF_POLICY_ONNX="${{ env.MODELS_DIR }}/driving_off_policy.onnx"
|
[ -f "$f" ] && VISION_ONNX="$f" && break
|
||||||
ON_POLICY_ONNX="${{ env.MODELS_DIR }}/driving_on_policy.onnx"
|
done
|
||||||
|
|
||||||
|
POLICY_ONNX=""
|
||||||
|
for f in "${{ env.MODELS_DIR }}/driving_policy.onnx" "${{ env.MODELS_DIR }}/big_driving_policy.onnx"; do
|
||||||
|
[ -f "$f" ] && POLICY_ONNX="$f" && break
|
||||||
|
done
|
||||||
|
|
||||||
|
OFF_POLICY_ONNX=""
|
||||||
|
for f in "${{ env.MODELS_DIR }}/driving_off_policy.onnx" "${{ env.MODELS_DIR }}/big_driving_off_policy.onnx"; do
|
||||||
|
[ -f "$f" ] && OFF_POLICY_ONNX="$f" && break
|
||||||
|
done
|
||||||
|
|
||||||
|
ON_POLICY_ONNX=""
|
||||||
|
for f in "${{ env.MODELS_DIR }}/driving_on_policy.onnx" "${{ env.MODELS_DIR }}/big_driving_on_policy.onnx"; do
|
||||||
|
[ -f "$f" ] && ON_POLICY_ONNX="$f" && break
|
||||||
|
done
|
||||||
|
|
||||||
SUPERCOMBO_ONNX=""
|
SUPERCOMBO_ONNX=""
|
||||||
for f in "${{ env.MODELS_DIR }}/supercombo.onnx" "${{ env.MODELS_DIR }}/driving_supercombo.onnx"; do
|
for f in "${{ env.MODELS_DIR }}/supercombo.onnx" "${{ env.MODELS_DIR }}/driving_supercombo.onnx" "${{ env.MODELS_DIR }}/big_supercombo.onnx" "${{ env.MODELS_DIR }}/big_driving_supercombo.onnx"; do
|
||||||
if [ -f "$f" ]; then
|
[ -f "$f" ] && SUPERCOMBO_ONNX="$f" && break
|
||||||
SUPERCOMBO_ONNX="$f"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
|
|
||||||
MODEL_TYPE="" ONNX_ARGS="" OUTPUT_NAME=""
|
MODEL_TYPE="" ONNX_ARGS="" OUTPUT_NAME=""
|
||||||
|
|||||||
@@ -272,18 +272,17 @@ def _parse_size(size_str: str) -> tuple[int, int]:
|
|||||||
return int(width), int(height)
|
return int(width), int(height)
|
||||||
|
|
||||||
|
|
||||||
def read_file_chunked_to_shm(path):
|
def read_file_chunked_to_disk(path):
|
||||||
if not path:
|
if not path:
|
||||||
return None
|
return None
|
||||||
import atexit
|
import atexit
|
||||||
import shutil
|
import shutil
|
||||||
from openpilot.common.file_chunker import open_file_chunked
|
from openpilot.common.file_chunker import open_file_chunked
|
||||||
from openpilot.common.hardware.hw import Paths
|
tmp_path = f'{path}.unchunked'
|
||||||
shm_path = os.path.join(Paths.shm_path(), os.path.basename(path))
|
with open(tmp_path, 'wb') as f, open_file_chunked(path) as src:
|
||||||
atexit.register(lambda: os.path.exists(shm_path) and os.remove(shm_path))
|
shutil.copyfileobj(src, f)
|
||||||
with open(shm_path, 'wb') as dst, open_file_chunked(path) as src:
|
atexit.register(lambda: os.path.exists(tmp_path) and os.remove(tmp_path))
|
||||||
shutil.copyfileobj(src, dst)
|
return tmp_path
|
||||||
return shm_path
|
|
||||||
|
|
||||||
|
|
||||||
def _load_policy_runners(args: argparse.Namespace) -> tuple[list, list]:
|
def _load_policy_runners(args: argparse.Namespace) -> tuple[list, list]:
|
||||||
@@ -327,11 +326,11 @@ if __name__ == "__main__":
|
|||||||
model_w, model_h = args.model_size
|
model_w, model_h = args.model_size
|
||||||
output_data = {}
|
output_data = {}
|
||||||
|
|
||||||
args.vision_onnx = read_file_chunked_to_shm(args.vision_onnx)
|
args.vision_onnx = read_file_chunked_to_disk(args.vision_onnx)
|
||||||
args.policy_onnx = read_file_chunked_to_shm(args.policy_onnx)
|
args.policy_onnx = read_file_chunked_to_disk(args.policy_onnx)
|
||||||
args.off_policy_onnx = read_file_chunked_to_shm(args.off_policy_onnx)
|
args.off_policy_onnx = read_file_chunked_to_disk(args.off_policy_onnx)
|
||||||
args.on_policy_onnx = read_file_chunked_to_shm(args.on_policy_onnx)
|
args.on_policy_onnx = read_file_chunked_to_disk(args.on_policy_onnx)
|
||||||
args.supercombo_onnx = read_file_chunked_to_shm(args.supercombo_onnx)
|
args.supercombo_onnx = read_file_chunked_to_disk(args.supercombo_onnx)
|
||||||
|
|
||||||
vision_runner = OnnxRunner(args.vision_onnx) if args.vision_onnx else None
|
vision_runner = OnnxRunner(args.vision_onnx) if args.vision_onnx else None
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,15 @@ This file is part of sunnypilot and is licensed under the MIT License.
|
|||||||
See the LICENSE.md file in the root directory for more details.
|
See the LICENSE.md file in the root directory for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from openpilot.common.parameterized import parameterized
|
from openpilot.common.parameterized import parameterized
|
||||||
|
|
||||||
from openpilot.sunnypilot.modeld_v2.compile_modeld import derive_frame_skip, _detect_desire_key
|
from openpilot.common.file_chunker import chunk_file, get_chunk_targets
|
||||||
|
from openpilot.sunnypilot.modeld_v2.compile_modeld import derive_frame_skip, _detect_desire_key, read_file_chunked_to_disk
|
||||||
from openpilot.common.test import OpenpilotTestCase
|
from openpilot.common.test import OpenpilotTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -160,3 +165,33 @@ class TestOutputSlicePreservation(OpenpilotTestCase):
|
|||||||
policy_slices = {'plan': slice(0, 495), 'meta': slice(495, 550)}
|
policy_slices = {'plan': slice(0, 495), 'meta': slice(495, 550)}
|
||||||
assert set(vision_slices.keys()) & set(policy_slices.keys()) == set(), \
|
assert set(vision_slices.keys()) & set(policy_slices.keys()) == set(), \
|
||||||
"vision and policy slices should not overlap in keys"
|
"vision and policy slices should not overlap in keys"
|
||||||
|
|
||||||
|
|
||||||
|
class TestReadFileChunkedToDisk(OpenpilotTestCase):
|
||||||
|
def test_none_passthrough(self):
|
||||||
|
assert read_file_chunked_to_disk(None) is None
|
||||||
|
|
||||||
|
def test_unchunked_source_staged_on_disk(self):
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
src = Path(d) / "driving_supercombo.onnx"
|
||||||
|
payload = os.urandom(1024)
|
||||||
|
src.write_bytes(payload)
|
||||||
|
|
||||||
|
out = Path(read_file_chunked_to_disk(str(src)))
|
||||||
|
|
||||||
|
assert out.parent == Path(d)
|
||||||
|
assert out.name == "driving_supercombo.onnx.unchunked"
|
||||||
|
assert out.read_bytes() == payload
|
||||||
|
|
||||||
|
def test_chunked_source_reassembled_on_disk(self):
|
||||||
|
with tempfile.TemporaryDirectory() as d:
|
||||||
|
src = Path(d) / "driving_supercombo.onnx"
|
||||||
|
payload = os.urandom(4096)
|
||||||
|
src.write_bytes(payload)
|
||||||
|
chunk_file(str(src), get_chunk_targets(str(src), len(payload)))
|
||||||
|
assert not src.exists()
|
||||||
|
|
||||||
|
out = Path(read_file_chunked_to_disk(str(src)))
|
||||||
|
|
||||||
|
assert out.parent == Path(d)
|
||||||
|
assert out.read_bytes() == payload
|
||||||
|
|||||||
@@ -4,7 +4,13 @@ import hashlib
|
|||||||
|
|
||||||
from openpilot.common.basedir import BASEDIR
|
from openpilot.common.basedir import BASEDIR
|
||||||
from openpilot.sunnypilot import get_file_hash
|
from openpilot.sunnypilot import get_file_hash
|
||||||
from openpilot.sunnypilot.models.model_name import DEFAULT_MODEL
|
from openpilot.selfdrive.modeld.helpers import usbgpu_present
|
||||||
|
from openpilot.sunnypilot.models.model_name import DEFAULT_MODEL, DEFAULT_BIG_MODEL
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_model() -> str:
|
||||||
|
return DEFAULT_BIG_MODEL if usbgpu_present() else DEFAULT_MODEL
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_MODEL_NAME_PATH = os.path.join(BASEDIR, "openpilot", "sunnypilot", "models", "model_name.py")
|
DEFAULT_MODEL_NAME_PATH = os.path.join(BASEDIR, "openpilot", "sunnypilot", "models", "model_name.py")
|
||||||
MODEL_HASH_PATH = os.path.join(BASEDIR, "openpilot", "sunnypilot", "models", "tests", "model_hash")
|
MODEL_HASH_PATH = os.path.join(BASEDIR, "openpilot", "sunnypilot", "models", "tests", "model_hash")
|
||||||
@@ -13,7 +19,6 @@ SUPERCOMBO_ONNX_PATH = os.path.join(BASEDIR, "openpilot", "selfdrive", "modeld",
|
|||||||
|
|
||||||
def update_model_hash():
|
def update_model_hash():
|
||||||
supercombo_hash = get_file_hash(SUPERCOMBO_ONNX_PATH)
|
supercombo_hash = get_file_hash(SUPERCOMBO_ONNX_PATH)
|
||||||
|
|
||||||
combined_hash = hashlib.sha256(supercombo_hash.encode()).hexdigest()
|
combined_hash = hashlib.sha256(supercombo_hash.encode()).hexdigest()
|
||||||
|
|
||||||
with open(MODEL_HASH_PATH, "w") as f:
|
with open(MODEL_HASH_PATH, "w") as f:
|
||||||
@@ -22,40 +27,28 @@ def update_model_hash():
|
|||||||
print(f"Generated and updated new combined model hash to {MODEL_HASH_PATH}")
|
print(f"Generated and updated new combined model hash to {MODEL_HASH_PATH}")
|
||||||
|
|
||||||
|
|
||||||
def get_current_default_model_name():
|
def update_default_model_names(default_model_name: str, default_big_model_name: str):
|
||||||
print("[GET DEFAULT MODEL NAME]")
|
print("[CHANGE DEFAULT MODEL NAMES]")
|
||||||
name = DEFAULT_MODEL
|
|
||||||
print(f'Current default model name: "{name}"')
|
|
||||||
|
|
||||||
return name
|
|
||||||
|
|
||||||
|
|
||||||
def update_default_model_name(name: str):
|
|
||||||
print("[CHANGE DEFAULT MODEL NAME]")
|
|
||||||
with open(DEFAULT_MODEL_NAME_PATH, "w") as f:
|
with open(DEFAULT_MODEL_NAME_PATH, "w") as f:
|
||||||
f.write(f'DEFAULT_MODEL = "{name}"\n')
|
f.write(f'DEFAULT_MODEL = "{default_model_name}"\n')
|
||||||
print(f'New default model name: "{name}"')
|
f.write(f'DEFAULT_BIG_MODEL = "{default_big_model_name}"\n')
|
||||||
|
|
||||||
|
print(f'New default small model name: "{default_model_name}"')
|
||||||
|
print(f'New default big model name: "{default_big_model_name}"')
|
||||||
print("[DONE]")
|
print("[DONE]")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Update default model name and hash")
|
parser = argparse.ArgumentParser(description="Update default model names and hash")
|
||||||
parser.add_argument("--new_name", type=str, help="New default model name")
|
parser.add_argument("--new_small_model_name", type=str, help="New default small model name")
|
||||||
|
parser.add_argument("--new_big_model_name", type=str, help="New default big model name")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if not args.new_name:
|
if args.new_small_model_name is None and args.new_big_model_name is None:
|
||||||
print("Warning: No new default model name provided. Use --new_name to specify")
|
new_name = input(f'Enter new default small model name (current: "{DEFAULT_MODEL}", leave empty to keep): ').strip()
|
||||||
print("Default model name and hash will not be updated! (aborted)")
|
new_big_model_name = input(f'Enter new default big model name (current: "{DEFAULT_BIG_MODEL}", leave empty to keep): ').strip()
|
||||||
exit(0)
|
else:
|
||||||
|
new_name, new_big_model_name = args.new_small_model_name, args.new_big_model_name
|
||||||
|
|
||||||
current_name = get_current_default_model_name()
|
update_default_model_names(new_name or DEFAULT_MODEL, new_big_model_name or DEFAULT_BIG_MODEL)
|
||||||
new_name = args.new_name
|
|
||||||
if current_name == new_name:
|
|
||||||
print(f'Proposed default model name: "{new_name}"')
|
|
||||||
confirm = input("Proposed default model name is the same as the current default model name. Confirm? (y/n): ").upper().strip()
|
|
||||||
if confirm != "Y":
|
|
||||||
print("Default model name and hash will not be updated! (aborted)")
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
update_default_model_name(new_name)
|
|
||||||
update_model_hash()
|
update_model_hash()
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class ModelCache:
|
|||||||
class ModelFetcher:
|
class ModelFetcher:
|
||||||
"""Handles fetching and caching of model data from remote source"""
|
"""Handles fetching and caching of model data from remote source"""
|
||||||
MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_v20.json"
|
MODEL_URL = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_v20.json"
|
||||||
MODEL_URL_USBGPU = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_usbgpu_v20.json"
|
MODEL_URL_USBGPU = "https://raw.githubusercontent.com/sunnypilot/sunnypilot-models/refs/heads/gh-pages/docs/driving_models_usbgpu_v21.json"
|
||||||
|
|
||||||
def __init__(self, params: Params):
|
def __init__(self, params: Params):
|
||||||
self.params = params
|
self.params = params
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
DEFAULT_MODEL = "CD210"
|
DEFAULT_MODEL = "CD210"
|
||||||
|
DEFAULT_BIG_MODEL = "Lebowski"
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ class TestDefaultModel(OpenpilotTestCase):
|
|||||||
with open(MODEL_HASH_PATH) as f:
|
with open(MODEL_HASH_PATH) as f:
|
||||||
current_hash = f.read().strip()
|
current_hash = f.read().strip()
|
||||||
|
|
||||||
assert combined_hash == current_hash, "Run sunnypilot/models/default_model.py to update the default model name and hash"
|
assert combined_hash == current_hash, "Run openpilot/sunnypilot/models/default_model.py to update the default model name and hash"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from websocket import (ABNF, WebSocket, WebSocketException, WebSocketTimeoutExce
|
|||||||
create_connection, WebSocketConnectionClosedException)
|
create_connection, WebSocketConnectionClosedException)
|
||||||
|
|
||||||
import openpilot.cereal.messaging as messaging
|
import openpilot.cereal.messaging as messaging
|
||||||
from openpilot.sunnypilot.models.default_model import DEFAULT_MODEL
|
from openpilot.sunnypilot.models.default_model import get_default_model
|
||||||
from openpilot.sunnypilot.selfdrive.car.sync_sunnylink_params import update_car_list_param
|
from openpilot.sunnypilot.selfdrive.car.sync_sunnylink_params import update_car_list_param
|
||||||
from openpilot.sunnypilot.sunnylink.api import SunnylinkApi
|
from openpilot.sunnypilot.sunnylink.api import SunnylinkApi
|
||||||
from openpilot.sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte, save_param_from_base64_encoded_string
|
from openpilot.sunnypilot.sunnylink.utils import sunnylink_need_register, sunnylink_ready, get_param_as_byte, save_param_from_base64_encoded_string
|
||||||
@@ -181,7 +181,7 @@ def getParamsMetadata() -> str:
|
|||||||
schema = generate_schema()
|
schema = generate_schema()
|
||||||
schema["capabilities"] = generate_capabilities()
|
schema["capabilities"] = generate_capabilities()
|
||||||
schema["capability_labels"] = CAPABILITY_LABELS
|
schema["capability_labels"] = CAPABILITY_LABELS
|
||||||
schema["default_model"] = DEFAULT_MODEL
|
schema["default_model"] = get_default_model()
|
||||||
raw = json.dumps(schema, separators=(",", ":")).encode("utf-8")
|
raw = json.dumps(schema, separators=(",", ":")).encode("utf-8")
|
||||||
return base64.b64encode(gzip.compress(raw)).decode("utf-8")
|
return base64.b64encode(gzip.compress(raw)).decode("utf-8")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Define the service name
|
|
||||||
SERVICE_NAME="actions.runner.sunnypilot.$(uname -n)"
|
|
||||||
|
|
||||||
# Function to control the service
|
|
||||||
control_service() {
|
|
||||||
local action=$1 # Store the function argument in a local variable
|
|
||||||
sudo systemctl $action ${SERVICE_NAME}
|
|
||||||
}
|
|
||||||
|
|
||||||
service_exists_and_is_loaded() {
|
|
||||||
sudo systemctl status ${SERVICE_NAME} &>/dev/null
|
|
||||||
if [[ $? -ne 4 ]]; then
|
|
||||||
return 0 # Service is known to systemd (i.e., loaded)
|
|
||||||
else
|
|
||||||
return 1 # Service is unknown to systemd (i.e., not loaded)
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check for required argument
|
|
||||||
if [[ -z $1 ]] || { [[ $1 != "start" ]] && [[ $1 != "stop" ]]; }; then
|
|
||||||
echo "Usage: $0 {start|stop}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Store the script argument in a descriptive variable
|
|
||||||
ACTION=$1
|
|
||||||
|
|
||||||
# Trap EXIT signal (Ctrl+C) and stop the service
|
|
||||||
trap 'control_service stop ; exit' SIGINT SIGKILL EXIT
|
|
||||||
|
|
||||||
# Enter the main loop
|
|
||||||
while true; do
|
|
||||||
# Check if the service is actually present on the system
|
|
||||||
if service_exists_and_is_loaded; then
|
|
||||||
control_service $ACTION # Call the function with the specified action
|
|
||||||
fi
|
|
||||||
sleep 1 # Pause before the next iteration
|
|
||||||
done
|
|
||||||
@@ -68,10 +68,6 @@ def only_offroad(started: bool, params: Params, CP: car.CarParams) -> bool:
|
|||||||
def livestream(started: bool, params: Params, CP: car.CarParams) -> bool:
|
def livestream(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||||
return params.get_bool("IsLiveStreaming")
|
return params.get_bool("IsLiveStreaming")
|
||||||
|
|
||||||
def use_github_runner(started, params, CP: car.CarParams) -> bool:
|
|
||||||
return not PC and params.get_bool("EnableGithubRunner") and (
|
|
||||||
not params.get_bool("NetworkMetered") and not params.get_bool("GithubRunnerSufficientVoltage"))
|
|
||||||
|
|
||||||
def use_copyparty(started, params, CP: car.CarParams) -> bool:
|
def use_copyparty(started, params, CP: car.CarParams) -> bool:
|
||||||
return bool(params.get_bool("EnableCopyparty"))
|
return bool(params.get_bool("EnableCopyparty"))
|
||||||
|
|
||||||
@@ -189,10 +185,6 @@ procs += [
|
|||||||
NativeProcess("locationd_llk", "openpilot/sunnypilot/selfdrive/locationd", ["./locationd"], only_onroad),
|
NativeProcess("locationd_llk", "openpilot/sunnypilot/selfdrive/locationd", ["./locationd"], only_onroad),
|
||||||
]
|
]
|
||||||
|
|
||||||
if os.path.exists("./github_runner.sh"):
|
|
||||||
procs += [NativeProcess("github_runner_start", "openpilot/system/manager",
|
|
||||||
["./github_runner.sh", "start"], and_(only_offroad, use_github_runner), sigkill=False)]
|
|
||||||
|
|
||||||
if os.path.exists("../../sunnypilot/sunnylink/uploader.py"):
|
if os.path.exists("../../sunnypilot/sunnylink/uploader.py"):
|
||||||
procs += [PythonProcess("sunnylink_uploader", "openpilot.sunnypilot.sunnylink.uploader", use_sunnylink_uploader_shim)]
|
procs += [PythonProcess("sunnylink_uploader", "openpilot.sunnypilot.sunnylink.uploader", use_sunnylink_uploader_shim)]
|
||||||
|
|
||||||
|
|||||||
@@ -1,260 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Default values
|
|
||||||
DEFAULT_REPO_URL="https://github.com/sunnypilot"
|
|
||||||
START_AT_BOOT=false
|
|
||||||
RESTORE_MODE=false
|
|
||||||
RUNNER_VERSION="2.325.0"
|
|
||||||
|
|
||||||
# Parse command line arguments
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case $1 in
|
|
||||||
--start-at-boot)
|
|
||||||
START_AT_BOOT=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--token)
|
|
||||||
GITHUB_TOKEN="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--repo)
|
|
||||||
REPO_URL="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--restore)
|
|
||||||
RESTORE_MODE=true
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
if [ -z "$GITHUB_TOKEN" ]; then
|
|
||||||
GITHUB_TOKEN="$1"
|
|
||||||
elif [ -z "$REPO_URL" ]; then
|
|
||||||
REPO_URL="$1"
|
|
||||||
fi
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Determine BASE_DIR based on mount point
|
|
||||||
if mountpoint -q /data/media; then
|
|
||||||
BASE_DIR="/data/media/0/github"
|
|
||||||
else
|
|
||||||
BASE_DIR="/data/github"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Constants
|
|
||||||
RUNNER_USER="github-runner"
|
|
||||||
USER_GROUPS="comma,gpu,gpio,sudo"
|
|
||||||
RUNNER_DIR="${BASE_DIR}/runner"
|
|
||||||
BUILDS_DIR="${BASE_DIR}/builds"
|
|
||||||
LOGS_DIR="${BASE_DIR}/logs"
|
|
||||||
CACHE_DIR="${BASE_DIR}/cache"
|
|
||||||
OPENPILOT_DIR="${BASE_DIR}/openpilot"
|
|
||||||
|
|
||||||
# Basic utility functions (no dependencies)
|
|
||||||
remount_rw() {
|
|
||||||
sudo mount -o remount,rw /
|
|
||||||
}
|
|
||||||
|
|
||||||
remount_ro() {
|
|
||||||
sync || true # Try to sync but continue even if it fails
|
|
||||||
sudo mount -o remount,ro / # Always try to remount as read-only
|
|
||||||
}
|
|
||||||
|
|
||||||
# Always ensure we try to remount as read-only on exit
|
|
||||||
trap remount_ro EXIT
|
|
||||||
|
|
||||||
setup_runner_user() {
|
|
||||||
sudo useradd --comment 'GitHub Runner' --create-home --home-dir ${BASE_DIR} ${RUNNER_USER} --shell /bin/bash -G ${USER_GROUPS} || sudo usermod -aG ${USER_GROUPS} ${RUNNER_USER}
|
|
||||||
}
|
|
||||||
|
|
||||||
create_sudoers_entry() {
|
|
||||||
sudo grep -qxF "${RUNNER_USER} ALL=(ALL) NOPASSWD: ALL" /etc/sudoers || echo "${RUNNER_USER} ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers
|
|
||||||
}
|
|
||||||
|
|
||||||
set_directory_permissions() {
|
|
||||||
sudo chown -R ${RUNNER_USER}:comma "$BASE_DIR"
|
|
||||||
sudo chmod -R g+rwx "$BASE_DIR"
|
|
||||||
sudo find "$BASE_DIR" -type d -exec chmod g+s {} +
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_directories() {
|
|
||||||
echo "Creating necessary directories..."
|
|
||||||
sudo mkdir -p "$RUNNER_DIR" "$BUILDS_DIR" "$LOGS_DIR" "$CACHE_DIR" "$OPENPILOT_DIR"
|
|
||||||
mkdir -p "/data/openpilot"
|
|
||||||
sudo chown -R comma:comma "/data/openpilot"
|
|
||||||
sync
|
|
||||||
}
|
|
||||||
|
|
||||||
wipe_bash_logout() {
|
|
||||||
export BASE_DIR
|
|
||||||
sudo -u ${RUNNER_USER} bash -c "touch ${BASE_DIR}/.bash_logout"
|
|
||||||
sudo -u ${RUNNER_USER} bash -c "truncate -s 0 '${BASE_DIR}/.bash_logout'"
|
|
||||||
}
|
|
||||||
|
|
||||||
# System configuration functions (depends on basic utility functions)
|
|
||||||
setup_system_configs() {
|
|
||||||
echo "Setting up system configurations..."
|
|
||||||
remount_rw
|
|
||||||
setup_runner_user
|
|
||||||
create_sudoers_entry
|
|
||||||
remount_ro
|
|
||||||
set_directory_permissions
|
|
||||||
wipe_bash_logout
|
|
||||||
}
|
|
||||||
|
|
||||||
# Runner setup functions
|
|
||||||
install_runner() {
|
|
||||||
echo "Downloading and setting up runner..."
|
|
||||||
cd "$RUNNER_DIR"
|
|
||||||
curl -o actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz
|
|
||||||
sudo -u ${RUNNER_USER} tar -xzf ./actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz
|
|
||||||
sudo rm ./actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz
|
|
||||||
sudo chmod +x ./config.sh
|
|
||||||
}
|
|
||||||
|
|
||||||
configure_runner() {
|
|
||||||
remount_rw
|
|
||||||
echo "Configuring runner..."
|
|
||||||
cd "$RUNNER_DIR"
|
|
||||||
sudo -u ${RUNNER_USER} ./config.sh --url "$REPO_URL" --token "$GITHUB_TOKEN" --name $(hostname) --runnergroup "tici-tizi" --labels "tici" --work "$BUILDS_DIR" --unattended
|
|
||||||
remount_ro
|
|
||||||
}
|
|
||||||
|
|
||||||
create_service_template() {
|
|
||||||
echo "Creating service template..."
|
|
||||||
cat <<EOL > "$RUNNER_DIR/bin/actions.runner.service.template"
|
|
||||||
[Unit]
|
|
||||||
Description={{Description}}
|
|
||||||
After=network-online.target nss-lookup.target time-sync.target
|
|
||||||
Wants=network-online.target nss-lookup.target time-sync.target
|
|
||||||
StartLimitInterval=5
|
|
||||||
StartLimitBurst=10
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
User=root
|
|
||||||
ExecStart=/usr/bin/unshare -m -- /bin/bash -c 'mount --bind ${OPENPILOT_DIR} /data/openpilot && setpriv --reuid={{User}} --regid={{User}} --init-groups env HOME=${BASE_DIR} USER={{User}} LOGNAME={{User}} MAIL=/var/mail/{{User}} {{RunnerRoot}}/runsvc.sh'
|
|
||||||
WorkingDirectory={{RunnerRoot}}
|
|
||||||
KillMode=process
|
|
||||||
KillSignal=SIGTERM
|
|
||||||
TimeoutStopSec=5min
|
|
||||||
Restart=always
|
|
||||||
RestartSec=120
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
EOL
|
|
||||||
}
|
|
||||||
|
|
||||||
install_service() {
|
|
||||||
local service_name
|
|
||||||
if [ -f "${RUNNER_DIR}/.service" ]; then
|
|
||||||
service_name=$(cat "${RUNNER_DIR}/.service")
|
|
||||||
else
|
|
||||||
service_name="actions.runner.sunnypilot.$(uname -n)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
create_service_template
|
|
||||||
remount_rw
|
|
||||||
local service_path="/etc/systemd/system/${service_name}"
|
|
||||||
echo "Installing systemd service..."
|
|
||||||
if [ -f "${service_path}" ]; then
|
|
||||||
echo "Service ${service_path} found in systemd, we will delete it"
|
|
||||||
sudo rm -f "${service_path}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$RUNNER_DIR"
|
|
||||||
sudo ./svc.sh install $RUNNER_USER
|
|
||||||
|
|
||||||
if [ "$START_AT_BOOT" = false ]; then
|
|
||||||
sudo systemctl disable "${service_name}"
|
|
||||||
fi
|
|
||||||
remount_ro
|
|
||||||
}
|
|
||||||
|
|
||||||
check_restore_prerequisites() {
|
|
||||||
local can_restore=false
|
|
||||||
local service_name=""
|
|
||||||
|
|
||||||
# Check if base runner directory exists
|
|
||||||
if [ ! -d "${RUNNER_DIR}" ]; then
|
|
||||||
echo "ERROR: Runner directory ${RUNNER_DIR} does not exist"
|
|
||||||
echo "This directory is required for restore operations"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# First check if we have the required files for restoration
|
|
||||||
if [ -f "${RUNNER_DIR}/.credentials" ] && [ -f "${RUNNER_DIR}/.service" ]; then
|
|
||||||
can_restore=true
|
|
||||||
service_name=$(cat "${RUNNER_DIR}/.service")
|
|
||||||
echo "Found required runner configuration files"
|
|
||||||
else
|
|
||||||
echo "Missing required runner configuration files"
|
|
||||||
echo "Required: .credentials and .service files in ${RUNNER_DIR}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! id "${RUNNER_USER}" &>/dev/null; then
|
|
||||||
echo "User ${RUNNER_USER} does not exist"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Only proceed if we can restore AND need to restore
|
|
||||||
if [ "$can_restore" = true ]; then
|
|
||||||
echo "Restoration is possible"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "No restoration possible"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
perform_restore() {
|
|
||||||
echo "Starting runner restoration..."
|
|
||||||
setup_directories
|
|
||||||
setup_system_configs
|
|
||||||
install_service
|
|
||||||
echo "Runner restoration completed successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
perform_install() {
|
|
||||||
echo "Starting fresh installation..."
|
|
||||||
setup_directories
|
|
||||||
setup_system_configs
|
|
||||||
install_runner
|
|
||||||
set_directory_permissions
|
|
||||||
configure_runner
|
|
||||||
install_service
|
|
||||||
echo "Installation completed successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
if [ "$RESTORE_MODE" = true ]; then
|
|
||||||
echo "Running in restore mode - will only restore system configurations..."
|
|
||||||
check_restore_prerequisites
|
|
||||||
perform_restore
|
|
||||||
else
|
|
||||||
# Check required arguments for normal installation
|
|
||||||
if [ -z "$GITHUB_TOKEN" ]; then
|
|
||||||
echo "Usage: $0 [--start-at-boot] [--token <github_token>] [--repo <repository_url>] [--restore]"
|
|
||||||
echo "Required argument (except for --restore): github_token"
|
|
||||||
echo "Optional arguments:"
|
|
||||||
echo " --start-at-boot Enable auto-start at boot (default: false)"
|
|
||||||
echo " --repo Repository URL (default: ${DEFAULT_REPO_URL})"
|
|
||||||
echo " --restore Restore existing runner configuration"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set repository URL if not provided
|
|
||||||
REPO_URL="${REPO_URL:-$DEFAULT_REPO_URL}"
|
|
||||||
perform_install
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Starting runner service..."
|
|
||||||
cd "$RUNNER_DIR"
|
|
||||||
sudo ./svc.sh start
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
@@ -53,24 +53,28 @@ def create_pkl_name(full_name: str) -> str:
|
|||||||
return pkl
|
return pkl
|
||||||
|
|
||||||
|
|
||||||
def _read_pkl_bytes(pkl_path: Path) -> bytes:
|
def _hash_pkl(pkl_path: Path) -> str:
|
||||||
manifest = Path(f"{pkl_path}.chunkmanifest")
|
manifest = Path(f"{pkl_path}.chunkmanifest")
|
||||||
if manifest.exists():
|
if manifest.exists():
|
||||||
num_chunks = int(manifest.read_text().strip())
|
num_chunks = int(manifest.read_text().strip())
|
||||||
parts = []
|
paths = [Path(f"{pkl_path}.chunk{i + 1:02d}of{num_chunks:02d}") for i in range(num_chunks)]
|
||||||
for i in range(num_chunks):
|
else:
|
||||||
chunk = Path(f"{pkl_path}.chunk{i + 1:02d}of{num_chunks:02d}")
|
paths = [pkl_path]
|
||||||
parts.append(chunk.read_bytes())
|
|
||||||
return b''.join(parts)
|
digest = hashlib.sha256()
|
||||||
return pkl_path.read_bytes()
|
for path in paths:
|
||||||
|
with path.open('rb') as f:
|
||||||
|
while block := f.read(1024 * 1024):
|
||||||
|
digest.update(block)
|
||||||
|
return digest.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def _find_driving_pkl(output_path: Path) -> Path | None:
|
def _find_driving_pkl(output_path: Path) -> Path | None:
|
||||||
for pattern in ('driving_tinygrad.pkl', 'driving_*_tinygrad.pkl'):
|
for pattern in ('*driving_tinygrad.pkl', '*driving_*_tinygrad.pkl'):
|
||||||
matches = sorted(output_path.glob(pattern))
|
matches = sorted(output_path.glob(pattern))
|
||||||
if matches:
|
if matches:
|
||||||
return matches[0]
|
return matches[0]
|
||||||
for pattern in ('driving_tinygrad.pkl.chunkmanifest', 'driving_*_tinygrad.pkl.chunkmanifest'):
|
for pattern in ('*driving_tinygrad.pkl.chunkmanifest', '*driving_*_tinygrad.pkl.chunkmanifest'):
|
||||||
matches = sorted(output_path.glob(pattern))
|
matches = sorted(output_path.glob(pattern))
|
||||||
if matches:
|
if matches:
|
||||||
return Path(str(matches[0]).removesuffix('.chunkmanifest'))
|
return Path(str(matches[0]).removesuffix('.chunkmanifest'))
|
||||||
@@ -87,7 +91,7 @@ def _rename_pkl_with_chunks(old_pkl: Path, new_pkl: Path) -> Path:
|
|||||||
|
|
||||||
|
|
||||||
def generate_chunked_model(driving_pkl: Path) -> dict:
|
def generate_chunked_model(driving_pkl: Path) -> dict:
|
||||||
tinygrad_hash = hashlib.sha256(_read_pkl_bytes(driving_pkl)).hexdigest()
|
tinygrad_hash = _hash_pkl(driving_pkl)
|
||||||
|
|
||||||
chunks_config = []
|
chunks_config = []
|
||||||
manifest_file = Path(f"{driving_pkl}.chunkmanifest")
|
manifest_file = Path(f"{driving_pkl}.chunkmanifest")
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Determine BASE_DIR based on mount point
|
|
||||||
if mountpoint -q /data/media; then
|
|
||||||
GITHUB_BASE_DIR="/data/media/0/github"
|
|
||||||
else
|
|
||||||
GITHUB_BASE_DIR="/data/github"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Define directories and user
|
|
||||||
BIN_DIR="$GITHUB_BASE_DIR/bin"
|
|
||||||
BUILDS_DIR="$GITHUB_BASE_DIR/builds"
|
|
||||||
OPENPILOT_DIR="$GITHUB_BASE_DIR/openpilot"
|
|
||||||
LOGS_DIR="$GITHUB_BASE_DIR/logs"
|
|
||||||
CACHE_DIR="$GITHUB_BASE_DIR/cache"
|
|
||||||
RUNNER_USERNAME="github-runner"
|
|
||||||
# Define the systemd service name
|
|
||||||
SERVICE_NAME="github-runner"
|
|
||||||
USER_GROUPS="comma,gpu,gpio,sudo"
|
|
||||||
|
|
||||||
# Function to stop and disable the systemd service
|
|
||||||
stop_and_uninstall_service() {
|
|
||||||
cd $GITHUB_BASE_DIR/runner
|
|
||||||
sudo ./svc.sh stop
|
|
||||||
sudo ./svc.sh uninstall
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to remove the systemd service file
|
|
||||||
remove_runner() {
|
|
||||||
cd $GITHUB_BASE_DIR/runner
|
|
||||||
sudo rm .runner
|
|
||||||
sudo su -c './config.sh remove' github-runner
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to delete the Github Runner directories
|
|
||||||
delete_directories() {
|
|
||||||
sudo rm -rf "$BIN_DIR/github-runner"
|
|
||||||
sudo rm -rf "$GITHUB_BASE_DIR" "$BIN_DIR" "$BUILDS_DIR" "$LOGS_DIR" "$CACHE_DIR" "$OPENPILOT_DIR"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to remove the Github Runner user
|
|
||||||
delete_user() {
|
|
||||||
for group in ${USER_GROUPS//,/ }
|
|
||||||
do
|
|
||||||
sudo gpasswd -d ${RUNNER_USERNAME} ${group}
|
|
||||||
done
|
|
||||||
sudo userdel -r ${RUNNER_USERNAME}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to remove sudoers entry
|
|
||||||
remove_sudoers_entry() {
|
|
||||||
sudo sed -i.bak "/${RUNNER_USERNAME} ALL=(ALL) NOPASSWD: ALL/d" /etc/sudoers
|
|
||||||
}
|
|
||||||
|
|
||||||
# Make filesystem writable
|
|
||||||
sudo mount -o remount rw /
|
|
||||||
|
|
||||||
# Ensure filesystem is remounted as read-only on script exit
|
|
||||||
trap "sudo mount -o remount ro /" EXIT
|
|
||||||
|
|
||||||
# Call functions
|
|
||||||
stop_and_uninstall_service
|
|
||||||
remove_runner
|
|
||||||
delete_directories
|
|
||||||
delete_user
|
|
||||||
remove_sudoers_entry
|
|
||||||
# End of uninstall script
|
|
||||||
Reference in New Issue
Block a user