Skip to content
Merged

Dev #2149

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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 8 additions & 8 deletions .github/workflows/sync_php_files.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ on:
branches:
- dev
paths:
- 'apps/php/src/apt_install.sh'
- 'apps/php/src/cmd.sh'
- 'apps/php/src/extensions.ini'
- 'apps/php/src/php_extra.ini'
- 'apps/php/src/php_install.sh'
- 'archive/apps/php/src/apt_install.sh'
- 'archive/apps/php/src/cmd.sh'
- 'archive/apps/php/src/extensions.ini'
- 'archive/apps/php/src/php_extra.ini'
- 'archive/apps/php/src/php_install.sh'
jobs:
sync-php-files:
runs-on: ubuntu-latest
Expand All @@ -21,7 +21,7 @@ jobs:
- name: Sync files to apps/phpfpmapache/src
run: |
root_dir=$(pwd)
source_files_dir="$root_dir/apps/php/src"
source_files_dir="$root_dir/archive/apps/php/src"
destination_dir="$root_dir/apps/phpfpmapache/src"
file_list="apt_install.sh,cmd.sh,extensions.ini,php_extra.ini,php_install.sh"
for file in $(echo $file_list | tr ',' '\n'); do
Expand All @@ -31,7 +31,7 @@ jobs:
- name: Sync files to apps/phpfpmnginx/src
run: |
root_dir=$(pwd)
source_files_dir="$root_dir/apps/php/src"
source_files_dir="$root_dir/archive/apps/php/src"
destination_dir="$root_dir/apps/phpfpmnginx/src"
file_list="apt_install.sh,cmd.sh,extensions.ini,php_extra.ini,php_install.sh"
for file in $(echo $file_list | tr ',' '\n'); do
Expand All @@ -41,4 +41,4 @@ jobs:
- name: Commit and push changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Sync PHP Files
commit_message: Sync PHP Files
41 changes: 41 additions & 0 deletions .opencode/command/libs-batch-update-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
description: Implement approved updates for a batch of apps selected by app names or initials
agent: build
argument-hint: [app list | initials] [options optional]
---

Use the `batch-app-update` skill to run the batch app update workflow.

The slash command stays action-first (`libs-batch-update-app`) even though the shared skill label is `batch-app-update`.

Usage: `/libs-batch-update-app <selector>`

Supported selector forms:

- explicit app list, for example `n8n,nocodb,netdata`
- initials, for example `n,m` to target all apps whose names start with `n` or `m`
- mixed input when clear, for example `n8n,n,m`

Supported options:

- `--mode auto|app-list|initials`
- `--concurrency 1|2|3`
- `--continue`
- `--stop-on-first-failure`
- `--report`

Default behavior:

- selector mode defaults to `auto`
- concurrency defaults to `2`
- failure policy defaults to `continue`
- apps that appear to require reverse-engineering from an official `install.sh` without a clear official Compose spec, especially when the runtime likely exceeds 5 containers, should be skipped quickly and recorded in the batch summary for later manual follow-up
- after each app passes automated validation, run `libs appstore-sync --app <name>` so the updated package is synced into the appstore environment for the later manual E2E pass

If the task input is `help` or empty, echo the usage line, then ask the user for the selector.

If the task input is present, treat it as the workflow input.

If the task input contains `--report`, `formal`, or `formal-report`, produce the output using `report-template.md` from the skill.

$ARGUMENTS
2 changes: 1 addition & 1 deletion .opencode/command/libs-update-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The slash command stays action-first (`libs-update-app`) even though the shared

Usage: /libs-update-app <app name> [target version]

If the task input is `help` or empty, echo the usage line, then ask the user for the app name. The target version is optional: when omitted, detect the best candidate stable version, continue automatically when the choice is clear, and ask for confirmation only when the candidate is ambiguous, risky, or review-first.
If the task input is `help` or empty, echo the usage line, then ask the user for the app name. The target version is optional: when omitted, detect the best candidate stable version and continue automatically when the choice is clear. A major version jump by itself is not a reason to stop for confirmation, because this workflow updates repository packaging rather than performing a live-instance migration. Ask for confirmation only when the candidate is ambiguous, risky for the package itself, or review-first.

If the task input is present, treat it as the workflow input.

Expand Down
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ and versions prior to 0.8.0 do not strictly follow this format.
## [0.8.0] - Unreleased

### Added
- `libs dns-bind` / `libs dns-delete` for Aliyun DNS wildcard records, plus the `aliyun` provider in `make connector`
- `make dns-bind` / `make dns-delete` for Aliyun DNS wildcard records
- Generic DNS credentials in `make connector` (`dns` → `.secrets/dns.env` with `DNS_PROVIDER`/`DNS_KEY_ID`/`DNS_KEY_SECRET`/`DNS_DOMAIN`), plus cloud-provider options `alibabacloud` (`.secrets/aliyun.env`), `aws` (`.secrets/aws.env`), and `azure` (`.secrets/azure.env`)
- `make create-aliyun-ecs` to launch a spot Aliyun ECS instance from the fixed `ci/cloud/aliyun-ecs.json` config, with interactive disk size (bounded by the config range) and auto-release time; the marketplace `ImageId` is resolved from the product code via ECS `DescribeImages`, the instance is named `libs-test-<random>`, and the public IP is printed after creation
- Appstore Publish workflow with v2/catalog/library/manifest output model
- Channel-aware distribution merge for dev channel
- `workflow_dispatch` support for manual dev/rc/release publishing
- Contentful GraphQL catalog fetching (`build/fetch_catalog.py`)

### Changed
- Moved `dns-bind`, `dns-delete`, and `websoft9-upgrade` from `libs` commands to `make` targets (`make dns-bind`, `make dns-delete`, `make websoft9-upgrade`)
- Simplified `library.json` to only retain `Version` field
- GitHub Release tag now uses `library.json` Version instead of run_number
- Removed legacy `release.yml` and `release-dev.yml` workflows
Expand Down
106 changes: 93 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help opencode opencode-clear install libs cli remote connector test test-cli test-build test-skills
.PHONY: help opencode opencode-clear install libs cli remote connector dns-bind dns-delete websoft9-upgrade create-aliyun-ecs test test-cli test-build test-skills

ifeq ($(OS),Windows_NT)
PYTHON ?= py
Expand All @@ -18,6 +18,11 @@ help:
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'opencode-clear' 'list opencode sessions, confirm, then delete them (ARGS: --all/--dry-run)'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'cli' 'enter an activated shell for libs'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'libs' 'run one libs command, e.g. make libs ARGS="scan --app wordpress --json"'
@printf '\n \033[1;35m▸ Server Ops\033[0m\n'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'dns-bind' 'point the wildcard record (*.<domain>) at the remote host or localhost (ARGS: --domain/--ip/--json)'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'dns-delete' 'delete the single wildcard record (*.<domain>) for the domain (ARGS: --domain/--json)'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'websoft9-upgrade' 'upgrade the Websoft9 platform container: set image tag, pull, recreate (ARGS: --tag/--target)'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'create-aliyun-ecs' 'create a spot Aliyun ECS instance from ci/cloud/aliyun-ecs.json (ARGS: --disk-size/--release-in)'
@printf '\n \033[1;35m▸ Testing & Quality\033[0m\n'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'test' 'run the full repo machine-system test suite'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'test-cli' 'run cli unit and contract tests'
Expand All @@ -27,7 +32,7 @@ help:
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'help' 'show this help'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'install' 'create .venv and install the libs CLI'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'remote' 'interactively write .secrets/remote.env for remote-aware commands'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'connector' 'interactively write .secrets/<provider>.env for external API tokens'
@printf ' \033[2mmake \033[0m\033[1;32m%-18s\033[0m %s\n' 'connector' 'interactively write .secrets/<provider>.env for API tokens, DNS and cloud credentials'
@printf '\n'

opencode:
Expand All @@ -52,6 +57,26 @@ libs:
if [ -n "$$proxy" ]; then export https_proxy="$$proxy" http_proxy="$$proxy" all_proxy="$$proxy" no_proxy= NO_PROXY=; fi; \
$(VENV_BIN)/libs $(ARGS)

dns-bind:
@proxy="$${https_proxy:-$${HTTPS_PROXY:-$${http_proxy:-$${HTTP_PROXY:-}}}}"; \
if [ -n "$$proxy" ]; then export https_proxy="$$proxy" http_proxy="$$proxy" all_proxy="$$proxy" no_proxy= NO_PROXY=; fi; \
$(VENV_BIN)/python -m libs.ops dns-bind $(ARGS)

dns-delete:
@proxy="$${https_proxy:-$${HTTPS_PROXY:-$${http_proxy:-$${HTTP_PROXY:-}}}}"; \
if [ -n "$$proxy" ]; then export https_proxy="$$proxy" http_proxy="$$proxy" all_proxy="$$proxy" no_proxy= NO_PROXY=; fi; \
$(VENV_BIN)/python -m libs.ops dns-delete $(ARGS)

websoft9-upgrade:
@proxy="$${https_proxy:-$${HTTPS_PROXY:-$${http_proxy:-$${HTTP_PROXY:-}}}}"; \
if [ -n "$$proxy" ]; then export https_proxy="$$proxy" http_proxy="$$proxy" all_proxy="$$proxy" no_proxy= NO_PROXY=; fi; \
$(VENV_BIN)/python -m libs.ops websoft9-upgrade $(ARGS)

create-aliyun-ecs:
@proxy="$${https_proxy:-$${HTTPS_PROXY:-$${http_proxy:-$${HTTP_PROXY:-}}}}"; \
if [ -n "$$proxy" ]; then export https_proxy="$$proxy" http_proxy="$$proxy" all_proxy="$$proxy" no_proxy= NO_PROXY=; fi; \
$(VENV_BIN)/python -m libs.ops create-aliyun-ecs $(ARGS)

cli:
@echo "Entering a shell with the libs CLI activated. Type 'exit' to leave."
@exec bash -c 'source $(VENV_ACTIVATE); \
Expand Down Expand Up @@ -92,35 +117,90 @@ connector:
@mkdir -p .secrets
@bash -lc 'set -e; \
current_choice=1; \
if [ "${PROVIDER:-}" = "cloudflare" ] || [ "${PROVIDER:-}" = "2" ]; then current_choice=2; fi; \
if [ "${PROVIDER:-}" = "dockerhub" ] || [ "${PROVIDER:-}" = "3" ]; then current_choice=3; fi; \
if [ "${PROVIDER:-}" = "aliyun" ] || [ "${PROVIDER:-}" = "4" ]; then current_choice=4; fi; \
printf "Available providers:\n 1) contentful\n 2) cloudflare\n 3) dockerhub\n 4) aliyun (DNS)\n"; \
if [ "$${PROVIDER:-}" = "cloudflare" ] || [ "$${PROVIDER:-}" = "2" ]; then current_choice=2; fi; \
if [ "$${PROVIDER:-}" = "dockerhub" ] || [ "$${PROVIDER:-}" = "3" ]; then current_choice=3; fi; \
if [ "$${PROVIDER:-}" = "dns" ] || [ "$${PROVIDER:-}" = "4" ]; then current_choice=4; fi; \
if [ "$${PROVIDER:-}" = "alibabacloud" ] || [ "$${PROVIDER:-}" = "5" ]; then current_choice=5; fi; \
if [ "$${PROVIDER:-}" = "aws" ] || [ "$${PROVIDER:-}" = "6" ]; then current_choice=6; fi; \
if [ "$${PROVIDER:-}" = "azure" ] || [ "$${PROVIDER:-}" = "7" ]; then current_choice=7; fi; \
printf "Available providers:\n 1) contentful\n 2) cloudflare\n 3) dockerhub\n 4) dns\n 5) alibabacloud\n 6) aws\n 7) azure\n"; \
read -r -p "provider [$$current_choice]: " input_choice; input_choice="$${input_choice:-$$current_choice}"; \
case "$$input_choice" in \
1|contentful) provider="contentful"; file=".secrets/contentful.env"; key="CONTENTFUL_ACCESS_TOKEN" ;; \
2|cloudflare) provider="cloudflare"; file=".secrets/cloudflare.env"; key="CLOUDFLARE_API_TOKEN" ;; \
3|dockerhub) provider="dockerhub"; file=".secrets/dockerhub.env"; key="DOCKERHUB_TOKEN" ;; \
4|aliyun) provider="aliyun"; file=".secrets/aliyun.env"; key="ALIYUN_ACCESS_KEY_SECRET" ;; \
4|dns) provider="dns"; file=".secrets/dns.env" ;; \
5|alibabacloud) provider="aliyun"; file=".secrets/aliyun.env" ;; \
6|aws) provider="aws"; file=".secrets/aws.env" ;; \
7|azure) provider="azure"; file=".secrets/azure.env" ;; \
*) echo "unsupported provider selection: $$input_choice" >&2; exit 1 ;; \
esac; \
if [ -f "$$file" ]; then echo "updating $$file"; else echo "creating $$file"; fi; \
if [ "$$provider" = "aliyun" ]; then \
cur_id=""; cur_secret=""; cur_domain=""; \
if [ "$$provider" = "dns" ]; then \
cur_dns=""; cur_id=""; cur_secret=""; cur_domain=""; \
if [ -f "$$file" ]; then \
cur_dns="$$(grep -E '^DNS_PROVIDER=' "$$file" | cut -d= -f2-)"; \
cur_id="$$(grep -E '^DNS_KEY_ID=' "$$file" | cut -d= -f2-)"; \
cur_secret="$$(grep -E '^DNS_KEY_SECRET=' "$$file" | cut -d= -f2-)"; \
cur_domain="$$(grep -E '^DNS_DOMAIN=' "$$file" | cut -d= -f2-)"; \
fi; \
cur_dns="$${cur_dns:-aliyun}"; \
read -r -p "DNS_PROVIDER (e.g. aliyun) [$$cur_dns]: " input_dns; input_dns="$${input_dns:-$$cur_dns}"; \
read -r -p "DNS_KEY_ID [$$cur_id]: " input_id; input_id="$${input_id:-$$cur_id}"; \
read -r -s -p "DNS_KEY_SECRET [keep existing]: " input_secret; echo; input_secret="$${input_secret:-$$cur_secret}"; \
read -r -p "DNS_DOMAIN (wildcard base, e.g. libs.websoft9.cn) [$$cur_domain]: " input_domain; input_domain="$${input_domain:-$$cur_domain}"; \
if [ -z "$$input_dns" ] || [ -z "$$input_id" ] || [ -z "$$input_secret" ]; then echo "provider, key id and secret are required" >&2; exit 1; fi; \
if [ -n "$$input_domain" ]; then \
printf "DNS_PROVIDER=%s\nDNS_KEY_ID=%s\nDNS_KEY_SECRET=%s\nDNS_DOMAIN=%s\n" "$$input_dns" "$$input_id" "$$input_secret" "$$input_domain" > "$$file"; \
else \
printf "DNS_PROVIDER=%s\nDNS_KEY_ID=%s\nDNS_KEY_SECRET=%s\n" "$$input_dns" "$$input_id" "$$input_secret" > "$$file"; \
fi; \
elif [ "$$provider" = "aliyun" ]; then \
cur_id=""; cur_secret=""; cur_region=""; \
if [ -f "$$file" ]; then \
cur_id="$$(grep -E '^ALIYUN_ACCESS_KEY_ID=' "$$file" | cut -d= -f2-)"; \
cur_secret="$$(grep -E '^ALIYUN_ACCESS_KEY_SECRET=' "$$file" | cut -d= -f2-)"; \
cur_domain="$$(grep -E '^ALIYUN_DNS_DOMAIN=' "$$file" | cut -d= -f2-)"; \
cur_region="$$(grep -E '^ALIYUN_REGION=' "$$file" | cut -d= -f2-)"; \
fi; \
read -r -p "ALIYUN_ACCESS_KEY_ID [$$cur_id]: " input_id; input_id="$${input_id:-$$cur_id}"; \
read -r -s -p "ALIYUN_ACCESS_KEY_SECRET [keep existing]: " input_secret; echo; input_secret="$${input_secret:-$$cur_secret}"; \
read -r -p "ALIYUN_DNS_DOMAIN (wildcard base, e.g. libs.websoft9.cn) [$$cur_domain]: " input_domain; input_domain="$${input_domain:-$$cur_domain}"; \
read -r -p "ALIYUN_REGION (optional, e.g. cn-hongkong) [$$cur_region]: " input_region; input_region="$${input_region:-$$cur_region}"; \
if [ -z "$$input_id" ] || [ -z "$$input_secret" ]; then echo "access key id and secret are required" >&2; exit 1; fi; \
if [ -n "$$input_domain" ]; then \
printf "ALIYUN_ACCESS_KEY_ID=%s\nALIYUN_ACCESS_KEY_SECRET=%s\nALIYUN_DNS_DOMAIN=%s\n" "$$input_id" "$$input_secret" "$$input_domain" > "$$file"; \
if [ -n "$$input_region" ]; then \
printf "ALIYUN_ACCESS_KEY_ID=%s\nALIYUN_ACCESS_KEY_SECRET=%s\nALIYUN_REGION=%s\n" "$$input_id" "$$input_secret" "$$input_region" > "$$file"; \
else \
printf "ALIYUN_ACCESS_KEY_ID=%s\nALIYUN_ACCESS_KEY_SECRET=%s\n" "$$input_id" "$$input_secret" > "$$file"; \
fi; \
elif [ "$$provider" = "aws" ]; then \
cur_id=""; cur_secret=""; cur_region=""; \
if [ -f "$$file" ]; then \
cur_id="$$(grep -E '^AWS_ACCESS_KEY_ID=' "$$file" | cut -d= -f2-)"; \
cur_secret="$$(grep -E '^AWS_SECRET_ACCESS_KEY=' "$$file" | cut -d= -f2-)"; \
cur_region="$$(grep -E '^AWS_REGION=' "$$file" | cut -d= -f2-)"; \
fi; \
read -r -p "AWS_ACCESS_KEY_ID [$$cur_id]: " input_id; input_id="$${input_id:-$$cur_id}"; \
read -r -s -p "AWS_SECRET_ACCESS_KEY [keep existing]: " input_secret; echo; input_secret="$${input_secret:-$$cur_secret}"; \
read -r -p "AWS_REGION (optional, e.g. us-east-1) [$$cur_region]: " input_region; input_region="$${input_region:-$$cur_region}"; \
if [ -z "$$input_id" ] || [ -z "$$input_secret" ]; then echo "access key id and secret are required" >&2; exit 1; fi; \
if [ -n "$$input_region" ]; then \
printf "AWS_ACCESS_KEY_ID=%s\nAWS_SECRET_ACCESS_KEY=%s\nAWS_REGION=%s\n" "$$input_id" "$$input_secret" "$$input_region" > "$$file"; \
else \
printf "AWS_ACCESS_KEY_ID=%s\nAWS_SECRET_ACCESS_KEY=%s\n" "$$input_id" "$$input_secret" > "$$file"; \
fi; \
elif [ "$$provider" = "azure" ]; then \
cur_sub=""; cur_tenant=""; cur_client=""; cur_secret=""; \
if [ -f "$$file" ]; then \
cur_sub="$$(grep -E '^AZURE_SUBSCRIPTION_ID=' "$$file" | cut -d= -f2-)"; \
cur_tenant="$$(grep -E '^AZURE_TENANT_ID=' "$$file" | cut -d= -f2-)"; \
cur_client="$$(grep -E '^AZURE_CLIENT_ID=' "$$file" | cut -d= -f2-)"; \
cur_secret="$$(grep -E '^AZURE_CLIENT_SECRET=' "$$file" | cut -d= -f2-)"; \
fi; \
read -r -p "AZURE_SUBSCRIPTION_ID [$$cur_sub]: " input_sub; input_sub="$${input_sub:-$$cur_sub}"; \
read -r -p "AZURE_TENANT_ID [$$cur_tenant]: " input_tenant; input_tenant="$${input_tenant:-$$cur_tenant}"; \
read -r -p "AZURE_CLIENT_ID [$$cur_client]: " input_client; input_client="$${input_client:-$$cur_client}"; \
read -r -s -p "AZURE_CLIENT_SECRET [keep existing]: " input_secret; echo; input_secret="$${input_secret:-$$cur_secret}"; \
if [ -z "$$input_sub" ] || [ -z "$$input_tenant" ] || [ -z "$$input_client" ] || [ -z "$$input_secret" ]; then echo "subscription id, tenant id, client id and client secret are required" >&2; exit 1; fi; \
printf "AZURE_SUBSCRIPTION_ID=%s\nAZURE_TENANT_ID=%s\nAZURE_CLIENT_ID=%s\nAZURE_CLIENT_SECRET=%s\n" "$$input_sub" "$$input_tenant" "$$input_client" "$$input_secret" > "$$file"; \
elif [ "$$provider" = "dockerhub" ]; then \
cur_user=""; cur_token=""; cur_org=""; \
if [ -f "$$file" ]; then \
Expand Down
12 changes: 7 additions & 5 deletions Notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ Docker Model Runner
1. 把 README/Notes 再补成“如何取默认登录邮箱、如何登录”的最终文案
2. 继续排查 workload-launcher 的重启原因,争取把日志风险也压到最低

InfluxDB 开源时序数据库
OpenClaw
Canvas LMS
Semaphore

Canvas LMS

cloudreve,compreface,commafeed, coze,dashy,ejbca, frigate, falcon

Expand All @@ -42,4 +39,9 @@ RustDesk
elizaOS
Nuclear

port define?
port define?

canvas 令牌无效
jfrog 的默认账号密码
nginxproxymanager 没有显示
elasticsearch 域名访问报错。大概原因是 es 默认有 https 和 http 两种机制。
Loading
Loading