Skip to content

Commit 8310aa8

Browse files
authored
feat: merge install scripts, improve theme handling, and enhance container UI (#328)
1 parent bfc7c6e commit 8310aa8

File tree

18 files changed

+403
-305
lines changed

18 files changed

+403
-305
lines changed

.github/workflows/add-sponsors.yml

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ name: Update Sponsors
22

33
on:
44
workflow_dispatch: # Manual trigger
5-
push: # Trigger on every push
6-
branches: [main, master, feature-develop, feat/develop]
75
schedule:
8-
- cron: '0 0 * * *' # Daily automatic update
6+
- cron: "0 0 * * *" # Daily automatic update
97

108
permissions:
119
contents: write
@@ -29,24 +27,23 @@ jobs:
2927
-X POST -d '{
3028
"query": "query { user(login:\"${{ github.repository_owner }}\") { sponsorshipsAsMaintainer(first: 100) { nodes { sponsor { login, avatarUrl, url } } } } }"
3129
}' https://api.github.com/graphql)
32-
30+
3331
# Save sponsors as markdown table (avatar and login with hyperlink)
3432
echo "## 🎗️ Sponsors" > sponsors.md
3533
echo "" >> sponsors.md
3634
echo "| Avatar | Sponsor |" >> sponsors.md
3735
echo "| ------ | ------- |" >> sponsors.md
38-
36+
3937
echo "$SPONSORS_JSON" | jq -r '.data.user.sponsorshipsAsMaintainer.nodes[] |
4038
"| [![](\(.sponsor.avatarUrl)&s=150)](\(.sponsor.url)) | [\(.sponsor.login)](\(.sponsor.url)) |"' >> sponsors.md
41-
39+
4240
echo "" >> sponsors.md
4341
echo "❤️ Thank you for your support!" >> sponsors.md
44-
42+
4543
# Count sponsors for conditional update
4644
SPONSOR_COUNT=$(echo "$SPONSORS_JSON" | jq '.data.user.sponsorshipsAsMaintainer.nodes | length')
4745
echo "sponsor_count=$SPONSOR_COUNT" >> $GITHUB_OUTPUT
4846
49-
5047
- name: Update README
5148
if: steps.fetch-sponsors.outputs.sponsor_count > 0
5249
run: |

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ poetry.lock
6262

6363
**/.DS_Store
6464
.DS_Store
65+
66+
artifacts/
67+
cli/nixopus.spec
68+
69+
**/.DS_Store

api/api/versions.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"version": "v1",
55
"status": "active",
6-
"release_date": "2025-08-12T22:37:46.401031+05:30",
6+
"release_date": "2025-08-14T18:50:02.928075+05:30",
77
"end_of_life": "0001-01-01T00:00:00Z",
88
"changes": [
99
"Initial API version"

api/internal/features/container/controller/init.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ package controller
22

33
import (
44
"context"
5+
"fmt"
6+
"strings"
7+
58
"github.com/raghavyuva/nixopus-api/internal/features/deploy/docker"
69
"github.com/raghavyuva/nixopus-api/internal/features/logger"
710
"github.com/raghavyuva/nixopus-api/internal/features/notification"
811
shared_storage "github.com/raghavyuva/nixopus-api/internal/storage"
12+
shared_types "github.com/raghavyuva/nixopus-api/internal/types"
913
)
1014

1115
type ContainerController struct {
@@ -30,3 +34,20 @@ func NewContainerController(
3034
notification: notificationManager,
3135
}
3236
}
37+
38+
func (c *ContainerController) isProtectedContainer(containerID string, action string) (*shared_types.Response, bool) {
39+
details, err := c.dockerService.GetContainerById(containerID)
40+
if err != nil {
41+
return nil, false
42+
}
43+
name := strings.ToLower(details.Name)
44+
if strings.Contains(name, "nixopus") {
45+
c.logger.Log(logger.Info, fmt.Sprintf("Skipping %s for protected container", action), details.Name)
46+
return &shared_types.Response{
47+
Status: "success",
48+
Message: "Operation skipped for protected container",
49+
Data: map[string]string{"status": "skipped"},
50+
}, true
51+
}
52+
return nil, false
53+
}

api/internal/features/container/controller/remove_container.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import (
1212
func (c *ContainerController) RemoveContainer(f fuego.ContextNoBody) (*shared_types.Response, error) {
1313
containerID := f.PathParam("container_id")
1414

15+
if resp, skipped := c.isProtectedContainer(containerID, "remove"); skipped {
16+
return resp, nil
17+
}
18+
1519
err := c.dockerService.RemoveContainer(containerID, container.RemoveOptions{Force: true})
1620
if err != nil {
1721
c.logger.Log(logger.Error, err.Error(), "")

api/internal/features/container/controller/start_container.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import (
1212
func (c *ContainerController) StartContainer(f fuego.ContextNoBody) (*shared_types.Response, error) {
1313
containerID := f.PathParam("container_id")
1414

15+
if resp, skipped := c.isProtectedContainer(containerID, "start"); skipped {
16+
return resp, nil
17+
}
18+
1519
err := c.dockerService.StartContainer(containerID, container.StartOptions{})
1620
if err != nil {
1721
c.logger.Log(logger.Error, err.Error(), "")

api/internal/features/container/controller/stop_container.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import (
1212
func (c *ContainerController) StopContainer(f fuego.ContextNoBody) (*shared_types.Response, error) {
1313
containerID := f.PathParam("container_id")
1414

15+
if resp, skipped := c.isProtectedContainer(containerID, "stop"); skipped {
16+
return resp, nil
17+
}
18+
1519
err := c.dockerService.StopContainer(containerID, container.StopOptions{})
1620
if err != nil {
1721
c.logger.Log(logger.Error, err.Error(), "")

assets/.DS_Store

-6 KB
Binary file not shown.

cli/app/commands/clone/clone.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def clone_repository(self, repo: str, path: str, branch: str = None) -> tuple[bo
4949
class GitCommandBuilder:
5050
@staticmethod
5151
def build_clone_command(repo: str, path: str, branch: str = None) -> list[str]:
52-
cmd = ["git", "clone"]
52+
cmd = ["git", "clone", "--depth=1"]
5353
if branch:
5454
cmd.extend(["-b", branch])
5555
cmd.extend([repo, path])
@@ -103,8 +103,8 @@ def __init__(self, logger: LoggerProtocol):
103103

104104
def clone_repository(self, repo: str, path: str, branch: str = None) -> tuple[bool, str]:
105105
cmd = GitCommandBuilder.build_clone_command(repo, path, branch)
106-
107-
self.logger.debug(debug_executing_git_clone.format(command=' '.join(cmd)))
106+
107+
self.logger.debug(debug_executing_git_clone.format(command=" ".join(cmd)))
108108

109109
try:
110110
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
@@ -216,8 +216,9 @@ def _create_result(self, success: bool, error: str = None) -> CloneResult:
216216

217217
def clone(self) -> CloneResult:
218218
import time
219+
219220
start_time = time.time()
220-
221+
221222
self.logger.debug(debug_cloning_repo.format(repo=self.config.repo, path=self.config.path, force=self.config.force))
222223

223224
if not self._validate_prerequisites():
@@ -227,7 +228,7 @@ def clone(self) -> CloneResult:
227228
return self._create_result(False, failed_to_prepare_target_directory)
228229

229230
success, error = self.cloner.clone_repository(self.config.repo, self.config.path, self.config.branch)
230-
231+
231232
duration = time.time() - start_time
232233
self.logger.debug(debug_clone_completed.format(duration=f"{duration:.2f}", success=success))
233234

cli/build.sh

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
set -e
44

5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
cd "$SCRIPT_DIR"
7+
58
RED='\033[0;31m'
69
GREEN='\033[0;32m'
710
YELLOW='\033[1;33m'
@@ -136,6 +139,52 @@ EOF
136139
log_success "Spec file created: $SPEC_FILE"
137140
}
138141

142+
run_pyinstaller_build() {
143+
# Ensure spec file exists even if manually deleted
144+
if [[ ! -f "$SPEC_FILE" ]]; then
145+
log_warning "Spec file missing; regenerating..."
146+
create_spec_file
147+
fi
148+
149+
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
150+
ARCH=$(uname -m)
151+
152+
if [[ "$OS" == "linux" ]] && command -v docker &> /dev/null && [[ -z "$NIXOPUS_DISABLE_DOCKER" ]]; then
153+
case $ARCH in
154+
x86_64)
155+
MANYLINUX_IMAGE="quay.io/pypa/manylinux2014_x86_64"
156+
PYTAG="cp311-cp311"
157+
;;
158+
aarch64|arm64)
159+
MANYLINUX_IMAGE="quay.io/pypa/manylinux2014_aarch64"
160+
PYTAG="cp311-cp311"
161+
;;
162+
*)
163+
MANYLINUX_IMAGE=""
164+
;;
165+
esac
166+
167+
if [[ -n "$MANYLINUX_IMAGE" ]]; then
168+
log_info "Building with PyInstaller inside $MANYLINUX_IMAGE for wide glibc compatibility..."
169+
docker run --rm -v "$PWD":/work -w /work "$MANYLINUX_IMAGE" bash -lc \
170+
"export PATH=/opt/python/$PYTAG/bin:\$PATH && \
171+
python3 -m pip install -U pip && \
172+
python3 -m pip install 'poetry==1.8.3' && \
173+
poetry install --with dev && \
174+
poetry run pyinstaller --clean --noconfirm $SPEC_FILE" || {
175+
log_error "Dockerized build failed"
176+
exit 1
177+
}
178+
return
179+
fi
180+
181+
log_warning "Unsupported arch $ARCH for manylinux; building on host (may require newer glibc)"
182+
fi
183+
184+
log_info "Building with PyInstaller on host..."
185+
poetry run pyinstaller --clean --noconfirm $SPEC_FILE
186+
}
187+
139188
build_wheel() {
140189
log_info "Building wheel package..."
141190

@@ -147,7 +196,7 @@ build_wheel() {
147196
build_binary() {
148197
log_info "Building binary"
149198

150-
poetry run pyinstaller --clean --noconfirm $SPEC_FILE
199+
run_pyinstaller_build
151200

152201
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
153202
ARCH=$(uname -m)

0 commit comments

Comments
 (0)