Initial commit

This commit is contained in:
Léo Haf 2025-04-25 12:43:01 +02:00 committed by GitHub
commit 3bc6fb0c29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 564 additions and 0 deletions

37
.github/workflows/buildService.yml vendored Normal file
View File

@ -0,0 +1,37 @@
name: Build Service
on:
workflow_dispatch:
pull_request:
paths-ignore: ['*.md']
branches: ['main', 'master']
push:
paths-ignore: ['*.md']
branches: ['main', 'master']
jobs:
BuildPackage:
runs-on: ubuntu-latest
steps:
- name: Prepare StartOS SDK
uses: Start9Labs/sdk@v1
- name: Checkout services repository
uses: actions/checkout@v4
- name: Build the service package
id: build
run: |
git submodule update --init --recursive
start-sdk init
make
PACKAGE_ID=$(yq -oy ".id" manifest.*)
echo "package_id=$PACKAGE_ID" >> $GITHUB_ENV
printf "\n SHA256SUM: $(sha256sum ${PACKAGE_ID}.s9pk) \n"
shell: bash
- name: Upload .s9pk
uses: actions/upload-artifact@v4
with:
name: ${{ env.package_id }}.s9pk
path: ./${{ env.package_id }}.s9pk

72
.github/workflows/releaseService.yml vendored Normal file
View File

@ -0,0 +1,72 @@
name: Release Service
on:
push:
tags:
- 'v*.*'
jobs:
ReleasePackage:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Prepare StartOS SDK
uses: Start9Labs/sdk@v1
- name: Checkout services repository
uses: actions/checkout@v4
- name: Build the service package
run: |
git submodule update --init --recursive
start-sdk init
make
- name: Setting package ID and title from the manifest
id: package
run: |
echo "package_id=$(yq -oy ".id" manifest.*)" >> $GITHUB_ENV
echo "package_title=$(yq -oy ".title" manifest.*)" >> $GITHUB_ENV
shell: bash
- name: Generate sha256 checksum
run: |
PACKAGE_ID=${{ env.package_id }}
printf "\n SHA256SUM: $(sha256sum ${PACKAGE_ID}.s9pk) \n"
sha256sum ${PACKAGE_ID}.s9pk > ${PACKAGE_ID}.s9pk.sha256
shell: bash
- name: Generate changelog
run: |
PACKAGE_ID=${{ env.package_id }}
echo "## What's Changed" > change-log.txt
yq -oy '.release-notes' manifest.* >> change-log.txt
echo "## SHA256 Hash" >> change-log.txt
echo '```' >> change-log.txt
sha256sum ${PACKAGE_ID}.s9pk >> change-log.txt
echo '```' >> change-log.txt
shell: bash
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: ${{ env.package_title }} ${{ github.ref_name }}
prerelease: true
body_path: change-log.txt
files: |
./${{ env.package_id }}.s9pk
./${{ env.package_id }}.s9pk.sha256
- name: Publish to Registry
env:
S9USER: ${{ secrets.S9USER }}
S9PASS: ${{ secrets.S9PASS }}
S9REGISTRY: ${{ secrets.S9REGISTRY }}
run: |
if [[ -z "$S9USER" || -z "$S9PASS" || -z "$S9REGISTRY" ]]; then
echo "Publish skipped: missing registry credentials."
else
start-sdk publish https://$S9USER:$S9PASS@$S9REGISTRY ${{ env.package_id }}.s9pk
fi

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.s9pk
scripts/*.js
.DS_Store
.vscode/
docker-images

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "hello-world"]
path = hello-world
url = https://github.com/Start9Labs/hello-world

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM alpine:3.20
RUN apk update
RUN apk add --no-cache tini && \
rm -f /var/cache/apk/*
ARG ARCH
ADD ./hello-world/target/${ARCH}-unknown-linux-musl/release/hello-world /usr/local/bin/hello-world
RUN chmod +x /usr/local/bin/hello-world
ADD ./docker_entrypoint.sh /usr/local/bin/docker_entrypoint.sh
RUN chmod a+x /usr/local/bin/docker_entrypoint.sh

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Start9 Labs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

81
Makefile Normal file
View File

@ -0,0 +1,81 @@
PKG_ID := $(shell yq e ".id" < manifest.yaml)
PKG_VERSION := $(shell yq e ".version" < manifest.yaml)
TS_FILES := $(shell find ./ -name \*.ts)
HELLO_WORLD_SRC := $(shell find ./hello-world/src) hello-world/Cargo.toml hello-world/Cargo.lock
# delete the target of a rule if it has changed and its recipe exits with a nonzero exit status
.DELETE_ON_ERROR:
all: verify
verify: $(PKG_ID).s9pk
@start-sdk verify s9pk $(PKG_ID).s9pk
@echo " Done!"
@echo " Filesize: $(shell du -h $(PKG_ID).s9pk) is ready"
install:
@if [ ! -f ~/.embassy/config.yaml ]; then echo "You must define \"host: http://server-name.local\" in ~/.embassy/config.yaml config file first."; exit 1; fi
@echo "\nInstalling to $$(grep -v '^#' ~/.embassy/config.yaml | cut -d'/' -f3) ...\n"
@[ -f $(PKG_ID).s9pk ] || ( $(MAKE) && echo "\nInstalling to $$(grep -v '^#' ~/.embassy/config.yaml | cut -d'/' -f3) ...\n" )
@start-cli package install $(PKG_ID).s9pk
clean:
rm -rf docker-images
rm -f $(PKG_ID).s9pk
rm -f scripts/*.js
clean-manifest:
@sed -i '' '/^[[:blank:]]*#/d' manifest.yaml
@echo; echo "Comments successfully removed from manifest.yaml file."; echo
# BEGIN REBRANDING
rebranding:
@read -p "Enter new package ID name (must be a single word): " NEW_PKG_ID; \
read -p "Enter new package title: " NEW_PKG_TITLE; \
find . \( -name "*.md" -o -name ".gitignore" -o -name "manifest.yaml" -o -name "*Service.yml" \) -type f -not -path "./hello-world/*" -exec sed -i '' -e "s/hello-world/$$NEW_PKG_ID/g; s/Hello World/$$NEW_PKG_TITLE/g" {} +; \
echo; echo "Rebranding complete."; echo " New package ID name is: $$NEW_PKG_ID"; \
echo " New package title is: $$NEW_PKG_TITLE"; \
sed -i '' -e '/^# BEGIN REBRANDING/,/^# END REBRANDING/ s/^#*/#/' Makefile
@echo; echo "Note: Rebranding code has been commented out in Makefile"; echo
# END REBRANDING
scripts/embassy.js: $(TS_FILES)
deno run --allow-read --allow-write --allow-env --allow-net scripts/bundle.ts
arm:
@rm -f docker-images/x86_64.tar
ARCH=aarch64 $(MAKE)
x86:
@rm -f docker-images/aarch64.tar
ARCH=x86_64 $(MAKE)
docker-images/aarch64.tar: Dockerfile docker_entrypoint.sh hello-world/target/aarch64-unknown-linux-musl/release/hello-world
ifeq ($(ARCH),x86_64)
else
mkdir -p docker-images
docker buildx build --tag start9/$(PKG_ID)/main:$(PKG_VERSION) --build-arg ARCH=aarch64 --platform=linux/arm64 -o type=docker,dest=docker-images/aarch64.tar .
endif
docker-images/x86_64.tar: Dockerfile docker_entrypoint.sh hello-world/target/x86_64-unknown-linux-musl/release/hello-world
ifeq ($(ARCH),aarch64)
else
mkdir -p docker-images
docker buildx build --tag start9/$(PKG_ID)/main:$(PKG_VERSION) --build-arg ARCH=x86_64 --platform=linux/amd64 -o type=docker,dest=docker-images/x86_64.tar .
endif
$(PKG_ID).s9pk: manifest.yaml instructions.md icon.png LICENSE scripts/embassy.js docker-images/aarch64.tar docker-images/x86_64.tar
ifeq ($(ARCH),aarch64)
@echo "start-sdk: Preparing aarch64 package ..."
else ifeq ($(ARCH),x86_64)
@echo "start-sdk: Preparing x86_64 package ..."
else
@echo "start-sdk: Preparing Universal Package ..."
endif
@start-sdk pack
hello-world/target/aarch64-unknown-linux-musl/release/hello-world: $(HELLO_WORLD_SRC)
docker run --rm -v ~/.cargo/registry:/root/.cargo/registry -v "$(shell pwd)"/hello-world:/home/rust/src messense/rust-musl-cross:aarch64-musl cargo build --release
hello-world/target/x86_64-unknown-linux-musl/release/hello-world: $(HELLO_WORLD_SRC)
docker run --rm -v ~/.cargo/registry:/root/.cargo/registry -v "$(shell pwd)"/hello-world:/home/rust/src messense/rust-musl-cross:x86_64-musl cargo build --release

121
README.md Normal file
View File

@ -0,0 +1,121 @@
<p align="center">
<img src="icon.png" alt="Project Logo" width="21%">
</p>
# Hello World for StartOS
Hello World is a simple, minimal project that serves as a template for creating a service that runs on StartOS. This repository creates the `s9pk` package that is installed to run `hello-world` on [StartOS](https://github.com/Start9Labs/start-os/). Learn more about service packaging in the [Developer Docs](https://start9.com/latest/developer-docs/).
## Dependencies
Install the system dependencies below to build this project by following the instructions in the provided links. You can find instructions on how to set up the appropriate build environment in the [Developer Docs](https://docs.start9.com/latest/developer-docs/packaging).
- [docker](https://docs.docker.com/get-docker)
- [docker-buildx](https://docs.docker.com/buildx/working-with-buildx/)
- [yq](https://mikefarah.gitbook.io/yq)
- [deno](https://deno.land/)
- [make](https://www.gnu.org/software/make/)
- [start-sdk](https://github.com/Start9Labs/start-os/tree/sdk/)
## Build environment
Prepare your StartOS build environment. In this example we are using Ubuntu 20.04.
1. Install docker
```
curl -fsSL https://get.docker.com | bash
sudo usermod -aG docker "$USER"
exec sudo su -l $USER
```
2. Set buildx as the default builder
```
docker buildx install
docker buildx create --use
```
3. Enable cross-arch emulated builds in docker
```
docker run --privileged --rm linuxkit/binfmt:v0.8
```
4. Install yq
```
sudo snap install yq
```
5. Install deno
```
sudo snap install deno
```
6. Install essentials build packages
```
sudo apt-get install -y build-essential openssl libssl-dev libc6-dev clang libclang-dev ca-certificates
```
7. Install Rust
```
curl https://sh.rustup.rs -sSf | sh
# Choose nr 1 (default install)
source $HOME/.cargo/env
```
8. Build and install start-sdk
```
git clone https://github.com/Start9Labs/start-os.git && \
cd start-os && git submodule update --init --recursive && \
make sdk
```
Initialize sdk & verify install
```
start-sdk init
start-sdk --version
```
Now you are ready to build the `hello-world` package!
## Cloning
Clone the project locally:
```
git clone https://github.com/Start9Labs/hello-world-startos.git
cd hello-world-startos
git submodule update --init --recursive
```
## Building
To build the `hello-world` package for all platforms using start-sdk, run the following command:
```
make
```
To build the `hello-world` package for a single platform using start-sdk, run:
```
# for amd64
make x86
```
or
```
# for arm64
make arm
```
## Installing (on StartOS)
Run the following commands to determine successful install:
> :information_source: Change server-name.local to your Start9 server address
```
start-cli auth login
# Enter your StartOS password
start-cli --host https://server-name.local package install hello-world.s9pk
```
If you already have your `start-cli` config file setup with a default `host`, you can install simply by running:
```
make install
```
> **Tip:** You can also install the hello-world.s9pk using **Sideload Service** under the **System > Manage** section.
### Verify Install
Go to your StartOS Services page, select **Hello World**, configure and start the service. Then, verify its interfaces are accessible.
**Done!**

4
docker_entrypoint.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
printf "\n\n [i] Starting Hello World ...\n\n"
exec tini hello-world

1
hello-world Submodule

@ -0,0 +1 @@
Subproject commit 48ce8a519b14eb6c32d770af66656e04a7866228

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

5
instructions.md Normal file
View File

@ -0,0 +1,5 @@
# Instructions for Hello World
Instructions go here. These appear to the user in the UI on the Service page under **Instructions**.
You are allowed to include basic [Markdown formatting](https://www.markdownguide.org/basic-syntax).

167
manifest.yaml Normal file
View File

@ -0,0 +1,167 @@
# Example written in yaml (toml and json are also acceptable)
# The package identifier used by the OS. This must be unique amongst all other known packages
id: hello-world
# A human readable service title
title: "Hello World"
# Service version - accepts up to four digits, where the last confirms to revisions necessary for StartOS - see documentation: https://github.com/Start9Labs/emver-rs. This value will change with each release of the service.
version: 0.3.5
# Release notes for the update - can be a string, paragraph or URL
release-notes: |
Revamped for StartOS 0.3.5.x
- Changed version to 0.3.5
- Updated manifest with correct OS name, GPU acceleration support, and supported architectures.
- Added release and build GitHub workflows.
- Added log message to announce when the service is starting.
- Added new optimized and standardized icon.
- Added simple healthcheck.
- Updated to the latest stable Alpine-based Docker image.
- Updated license file with the current year.
- Code cleanup
# The type of license for the project. Include the LICENSE in the root of the project directory. A license is required for a Start9 package.
license: MIT
# The repository URL for the package. This repo contains the manifest file (this), any scripts necessary for configuration, backups, actions, or health checks (more below). This key must exist. But could be embedded into the source repository.
wrapper-repo: "https://github.com/Start9Labs/hello-world-startos"
# The original project repository URL. There is no upstream repo in this example
upstream-repo: "https://github.com/Start9Labs/hello-world"
# URL to the support site / channel for the project. This key can be omitted if none exists, or it can link to the original project repository issues.
support-site: "https://docs.start9.com/"
# URL to the marketing site for the project. This key can be omitted if none exists, or it can link to the original project repository.
marketing-site: "https://start9.com/"
# The series of commands to build the project into an s9pk for arm64/v8. In this case we are using a Makefile with the simple build command "make".
build: ["make"]
# Human readable descriptors for the service. These are used throughout the StartOS user interface, primarily in the marketplace.
description:
# This is the first description visible to the user in the marketplace.
short: Example service
# This description will display with additional details in the service's individual marketplace page
long: |
Hello World is a bare-bones service that launches a web interface to say "Hello World", and nothing more.
# These assets are static files necessary for packaging the service for Start9 (into an s9pk). Each value is a path to the specified asset. If an asset is missing from this list, or otherwise denoted, it will be defaulted to the values denoted below.
assets:
# Default = LICENSE.md
license: LICENSE
# Default = icon.png
icon: icon.png
# Default = INSTRUCTIONS.md
instructions: instructions.md
# Default = image.tar
# docker-images: image.tar
# ----- This section commented out until we support long-running containers -----
# The main action for initializing the service. This can be script to utilize the eOS scripting apis, or docker.
# main:
# type: script
# # Defines the containers needed to run the main and mounted volumes
# containers:
# main:
# # Identifier for the main image volume, which will be used when other actions need to mount to this volume.
# image: main
# # Specifies where to mount the data volume(s), if there are any. Mounts for pointer dependency volumes are also denoted here. These are necessary if data needs to be read from / written to these volumes.
# mounts:
# # Specifies where on the service's file system its persistence directory should be mounted prior to service startup
# main: /root
# ----- END commented section -----
main:
# Docker is currently the only action implementation
type: docker
# Identifier for the main image volume, which will be used when other actions need to mount to this volume.
image: main
# The executable binary for starting the initialization action. For docker actions, this is typically a "docker_entrypoint.sh" file. See the Dockerfile and the docker_entrypoint.sh in this project for additional details.
entrypoint: "docker_entrypoint.sh"
# Any arguments that should be passed into the entrypoint executable
args: []
# Specifies where to mount the data volume(s), if there are any. Mounts for pointer dependency volumes are also denoted here. These are necessary if data needs to be read from / written to these volumes.
mounts:
# Specifies where on the service's file system its persistence directory should be mounted prior to service startup
main: /root
# Specifies whether GPU acceleration is enabled or not. False by default.
gpu-acceleration: false
# Defines what architectures will be supported by the service. This service supports x86_64 and aarch64 architectures.
hardware-requirements:
arch:
- x86_64
- aarch64
# This is where health checks would be defined - see a more advanced example in https://github.com/Start9Labs/start9-pages-startos
health-checks:
web-ui:
name: Web Interface
success-message: The Hello World is accessible
type: script
config: ~
properties: ~
# type: script
# This denotes any data, asset, or pointer volumes that should be connected when the "docker run" command is invoked
volumes:
# This is the image where files from the project asset directory will go
main:
type: data
# This specifies how to configure the port mapping for exposing the service over TOR and LAN (if applicable). Many interfaces can be specified depending on the needs of the service. If it can be launched over a Local Area Network connection, specify a `lan-config`. Otherwise, at minimum, a `tor-config` must be specified.
interfaces:
# This key is the internal name that the OS will use to configure the interface
main:
# A human readable name for display in the UI
name: User Interface
# A descriptive description of what the interface does
description: A simple user interface that is expected to display the text "Hello Word"
tor-config:
# Port mappings are from the external port to the internal container port
port-mapping:
80: "80"
# Port mappings are from the external port to the internal container port
lan-config:
443:
ssl: true
internal: 80
# Denotes if the service has a user interface to display
ui: true
# Denotes the protocol specifications used by this interface
protocols:
- tcp
- http
dependencies: {}
# Specifies how backups should be run for this service. The default StartOS provided option is to use the duplicity backup library on a system image (compat)
backup:
create:
# Currently, only docker actions are supported.
type: docker
# The docker image to use. In this case, a pre-loaded system image called compat
image: compat
# Required if the action uses a system image. The default value is false.
system: true
# The executable to run the command to begin the backup create process
entrypoint: compat
# Arguments to pass into the entrypoint executable. In this example, the full command run will be: `compat duplicity hello-world /mnt/backup /root/data`
args:
- duplicity
- create
- /mnt/backup
# For duplicity, the backup mount point needs to be something other than `/root`, so we default to `/root/data`
- /root/data
mounts:
# BACKUP is the default volume that is used for backups. This is whatever backup drive is mounted to the device, or a network filesystem.
# The value here donates where the mount point will be. The backup drive is mounted to this location.
BACKUP: "/mnt/backup"
main: "/root/data"
# The action to execute the backup restore functionality. Details for the keys below are the same as above.
restore:
type: docker
image: compat
system: true
entrypoint: compat
args:
- duplicity
- restore
- /mnt/backup
- /root/data
mounts:
BACKUP: "/mnt/backup"
main: "/root/data"
migrations:
from:
"*":
type: script
args: ["from"]
to:
"*":
type: script
args: ["to"]

6
scripts/bundle.ts Normal file
View File

@ -0,0 +1,6 @@
// scripts/bundle.ts
import { bundle } from "https://deno.land/x/emit@0.40.0/mod.ts";
const result = await bundle("scripts/embassy.ts");
await Deno.writeTextFile("scripts/embassy.js", result.code);

1
scripts/deps.ts Normal file
View File

@ -0,0 +1 @@
export * from "https://deno.land/x/embassyd_sdk@v0.3.3.0.11/mod.ts";

5
scripts/embassy.ts Normal file
View File

@ -0,0 +1,5 @@
export { setConfig } from "./procedures/setConfig.ts";
export { getConfig } from "./procedures/getConfig.ts";
export { properties } from "./procedures/properties.ts";
export { migration } from "./procedures/migrations.ts";
export { health } from "./procedures/healthChecks.ts";

View File

@ -0,0 +1,5 @@
// To utilize the default config system built, this file is required. It defines the *structure* of the configuration file. These structured options display as changeable UI elements within the "Config" section of the service details page in the StartOS UI.
import { compat, types as T } from "../deps.ts";
export const getConfig: T.ExpectedExports.getConfig = compat.getConfig({});

View File

@ -0,0 +1,7 @@
import { types as T, healthUtil } from "../deps.ts";
export const health: T.ExpectedExports.health = {
async "web-ui"(effects, duration) {
return healthUtil.checkWebUrl("http://hello-world.embassy:80")(effects, duration).catch(healthUtil.catchError(effects))
},
};

View File

@ -0,0 +1,4 @@
import { compat, types as T } from "../deps.ts";
export const migration: T.ExpectedExports.migration = compat.migrations
.fromMapping({}, "0.3.5" );

View File

@ -0,0 +1,3 @@
import { compat, types as T } from "../deps.ts";
export const properties: T.ExpectedExports.properties = compat.properties;

View File

@ -0,0 +1,5 @@
// This is where any configuration rules related to the configuration would go. These ensure that the user can only create a valid config.
import { compat, } from "../deps.ts";
export const setConfig = compat.setConfig;