Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 265e02119a | |||
| 82d454ade4 | |||
| a9b300d711 | |||
| fded3a04d4 | |||
| 0d897f17b5 | |||
| 216f6f83fe | |||
| c023bdccae | |||
| 78f3ad8fcc | |||
| 2e7a52ed15 | |||
| 221b3533e5 | |||
| 578dccd0cf | |||
| 0ecad475ef | |||
| d5789b79a6 | |||
| 45ce90f3cc | |||
| 3a817625c5 | |||
| d5abea48b3 | |||
| 1da69ac272 | |||
| bd2092a3ea | |||
| afb6fb8ac7 | |||
| 414e9f4c33 | |||
| 10035221fb | |||
| 52a6f4368c | |||
| e774dbc301 | |||
| 5bf582f3ad | |||
| 7398bb0a16 | |||
| 79d36df83c |
@@ -0,0 +1,92 @@
|
||||
name: PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://git.keligrubb.com/actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: https://git.keligrubb.com/actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run lint
|
||||
run: npm run lint
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://git.keligrubb.com/actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: https://git.keligrubb.com/actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: npm run test
|
||||
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.59.1-noble
|
||||
steps:
|
||||
- uses: https://git.keligrubb.com/actions/checkout@v6
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: https://git.keligrubb.com/actions/setup-node@v6
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Generate dev cert
|
||||
run: ./scripts/gen-dev-cert.sh
|
||||
|
||||
- name: Run e2e tests
|
||||
run: npm run test:e2e
|
||||
env:
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: "0"
|
||||
|
||||
docker-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://git.keligrubb.com/actions/checkout@v6
|
||||
|
||||
- name: Set Docker image tag
|
||||
id: image
|
||||
run: |
|
||||
REGISTRY="${GITHUB_SERVER_URL#https://}"
|
||||
REGISTRY="${REGISTRY#http://}"
|
||||
echo "tag=${REGISTRY}/${{ github.repository }}:latest" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: https://git.keligrubb.com/actions/docker-setup-buildx-action@v4
|
||||
|
||||
- name: Build (dry run)
|
||||
uses: https://git.keligrubb.com/actions/docker-build-push-action@v7
|
||||
env:
|
||||
# Keeps GITHUB_OUTPUT small; Gitea act-runner can choke on multiline
|
||||
# outputs when PR webhook payloads (e.g. Renovate bodies) are huge.
|
||||
DOCKER_BUILD_SUMMARY: "false"
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
provenance: false
|
||||
sbom: false
|
||||
tags: ${{ steps.image.outputs.tag }}
|
||||
@@ -0,0 +1,90 @@
|
||||
name: Push
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://git.keligrubb.com/actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.KESTRELOS_REPO_TOKEN }}
|
||||
|
||||
- name: Get PR description for changelog
|
||||
env:
|
||||
GITEA_REPO_TOKEN: ${{ secrets.KESTRELOS_REPO_TOKEN }}
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/microsoft*.list /etc/apt/sources.list.d/azure*.list 2>/dev/null || true
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq jq
|
||||
RESP=$(curl -sf -H "Authorization: token $GITEA_REPO_TOKEN" \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/commits/${{ github.sha }}/pull") || true
|
||||
if [ -n "$RESP" ]; then
|
||||
echo "$RESP" | jq -r '.body // empty' > .ci_pr_body 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Release (bump, tag, push, create release)
|
||||
env:
|
||||
CI_REPO_OWNER: ${{ github.actor }}
|
||||
CI_REPO_NAME: ${{ github.event.repository.name }}
|
||||
CI_FORGE_URL: ${{ github.server_url }}
|
||||
CI_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
GITEA_REPO_TOKEN: ${{ secrets.KESTRELOS_REPO_TOKEN }}
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/microsoft*.list /etc/apt/sources.list.d/azure*.list 2>/dev/null || true
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq git wget
|
||||
./scripts/release.sh
|
||||
|
||||
publish:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://git.keligrubb.com/actions/checkout@v6
|
||||
with:
|
||||
ref: main
|
||||
token: ${{ secrets.KESTRELOS_REPO_TOKEN }}
|
||||
|
||||
- name: Log in to container registry
|
||||
uses: https://git.keligrubb.com/actions/docker-login-action@v4
|
||||
with:
|
||||
registry: git.keligrubb.com
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.KESTRELOS_REPO_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: https://git.keligrubb.com/actions/docker-setup-buildx-action@v4
|
||||
|
||||
- name: Build Docker image
|
||||
uses: https://git.keligrubb.com/actions/docker-build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
load: true
|
||||
tags: kestrelos:built
|
||||
|
||||
- name: Push Docker image (version + latest)
|
||||
run: |
|
||||
VERSION=$(awk '/"version"/ { match($0, /[0-9]+\.[0-9]+\.[0-9]+/); print substr($0, RSTART, RLENGTH); exit }' package.json)
|
||||
case "$VERSION" in
|
||||
[0-9]*.[0-9]*.[0-9]*) ;;
|
||||
*) echo "error: package.json version must be x.y.z (got: $VERSION)"; exit 1 ;;
|
||||
esac
|
||||
REGISTRY="git.keligrubb.com"
|
||||
IMAGE="$REGISTRY/${{ github.repository }}"
|
||||
for tag in "$VERSION" latest; do
|
||||
docker tag kestrelos:built "$IMAGE:$tag"
|
||||
docker push "$IMAGE:$tag"
|
||||
done
|
||||
|
||||
- name: Set up Helm
|
||||
uses: https://git.keligrubb.com/actions/setup-helm@v5
|
||||
|
||||
- name: Package and push Helm chart
|
||||
env:
|
||||
GITEA_REPO_TOKEN: ${{ secrets.KESTRELOS_REPO_TOKEN }}
|
||||
run: |
|
||||
helm package helm/kestrelos
|
||||
for f in kestrelos-*.tgz; do
|
||||
curl -sf -u "${{ github.actor }}:$GITEA_REPO_TOKEN" -X POST --upload-file "$f" \
|
||||
"${{ github.server_url }}/api/packages/${{ github.actor }}/helm/api/charts"
|
||||
done
|
||||
@@ -1,41 +0,0 @@
|
||||
when:
|
||||
- event: pull_request
|
||||
|
||||
steps:
|
||||
- name: install
|
||||
image: node:24-slim
|
||||
depends_on: []
|
||||
commands:
|
||||
- npm ci
|
||||
|
||||
- name: lint
|
||||
image: node:24-slim
|
||||
depends_on: [install]
|
||||
commands:
|
||||
- npm run lint
|
||||
|
||||
- name: test
|
||||
image: node:24-slim
|
||||
depends_on: [install]
|
||||
commands:
|
||||
- npm run test
|
||||
|
||||
- name: e2e
|
||||
image: mcr.microsoft.com/playwright:v1.58.2-noble
|
||||
depends_on: [install]
|
||||
commands:
|
||||
- ./scripts/gen-dev-cert.sh
|
||||
- npm run test:e2e
|
||||
environment:
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: "0"
|
||||
|
||||
- name: docker-build
|
||||
image: woodpeckerci/plugin-kaniko
|
||||
depends_on: []
|
||||
settings:
|
||||
repo: ${CI_REPO_OWNER}/${CI_REPO_NAME}
|
||||
registry: git.keligrubb.com
|
||||
tags: latest
|
||||
dry-run: true
|
||||
single-snapshot: true
|
||||
cleanup: true
|
||||
@@ -1,36 +0,0 @@
|
||||
when:
|
||||
- event: push
|
||||
branch: main
|
||||
|
||||
steps:
|
||||
- name: release
|
||||
image: alpine
|
||||
commands:
|
||||
- apk add --no-cache git
|
||||
- ./scripts/release.sh
|
||||
environment:
|
||||
GITEA_REPO_TOKEN:
|
||||
from_secret: gitea_repo_token
|
||||
|
||||
- name: docker
|
||||
image: woodpeckerci/plugin-kaniko
|
||||
depends_on: [release]
|
||||
settings:
|
||||
repo: ${CI_REPO_OWNER}/${CI_REPO_NAME}
|
||||
registry: git.keligrubb.com
|
||||
username: ${CI_REPO_OWNER}
|
||||
password:
|
||||
from_secret: gitea_registry_token
|
||||
single-snapshot: true
|
||||
cleanup: true
|
||||
|
||||
- name: helm
|
||||
image: alpine/helm
|
||||
depends_on: [release]
|
||||
environment:
|
||||
GITEA_REGISTRY_TOKEN:
|
||||
from_secret: gitea_registry_token
|
||||
commands:
|
||||
- apk add --no-cache curl
|
||||
- helm package helm/kestrelos
|
||||
- curl -sf -u $CI_REPO_OWNER:$GITEA_REGISTRY_TOKEN -X POST --upload-file kestrelos-*.tgz https://git.keligrubb.com/api/packages/$CI_REPO_OWNER/helm/api/charts
|
||||
+109
@@ -1,3 +1,112 @@
|
||||
## [1.1.2] - 2026-04-15
|
||||
### Changed
|
||||
- Update dependency fast-xml-parser to v5.6.0 (#28)
|
||||
|
||||
This PR contains the following updates:
|
||||
|
||||
| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|
||||
|---|---|---|---|
|
||||
| [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) | [`5.5.12` → `5.6.0`](https://renovatebot.com/diffs/npm/fast-xml-parser/5.5.12/5.6.0) |  |  |
|
||||
|
||||
---
|
||||
|
||||
### Release Notes
|
||||
|
||||
<details>
|
||||
<summary>NaturalIntelligence/fast-xml-parser (fast-xml-parser)</summary>
|
||||
|
||||
### [`v5.6.0`](https://github.com/NaturalIntelligence/fast-xml-parser/releases/tag/v5.6.0): use @​nodable/entities to replace entities
|
||||
|
||||
[Compare Source](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.5.12...v5.6.0)
|
||||
|
||||
- No API change
|
||||
- No change in performance for basic usage
|
||||
- No typing change
|
||||
- No config change
|
||||
- new dependency
|
||||
- breaking: error messages for entities might have been changed.
|
||||
-
|
||||
|
||||
**Full Changelog**: <https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.5.12...v5.6.0>
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### Configuration
|
||||
|
||||
📅 **Schedule**: (UTC)
|
||||
|
||||
- Branch creation
|
||||
- At any time (no schedule defined)
|
||||
- Automerge
|
||||
- At any time (no schedule defined)
|
||||
|
||||
🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.
|
||||
|
||||
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
|
||||
|
||||
🔕 **Ignore**: Close this PR and you won't be reminded about this update again.
|
||||
|
||||
---
|
||||
|
||||
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
|
||||
|
||||
---
|
||||
|
||||
This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
|
||||
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMjAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEyMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
|
||||
|
||||
## [1.1.1] - 2026-04-15
|
||||
### Changed
|
||||
- split push release/publish and harden workflows (#27)
|
||||
|
||||
### Added
|
||||
* Separate release from Docker/Helm publish
|
||||
* enrich releases with PRbodies when available
|
||||
* tighten release.sh validation and idempotency
|
||||
* trim PR docker-build metadata for act-runner stability
|
||||
|
||||
## [1.1.0] - 2026-04-15
|
||||
### Changed
|
||||
- Update all non-major dependencies (#25)
|
||||
|
||||
## [1.0.10] - 2026-04-15
|
||||
### Changed
|
||||
- Remove npm overrides for tar (#26)
|
||||
|
||||
## [1.0.9] - 2026-03-24
|
||||
### Changed
|
||||
- update https://git.keligrubb.com/actions/setup-helm action to v5 (#23)
|
||||
|
||||
## [1.0.8] - 2026-03-12
|
||||
### Changed
|
||||
- fix release file (#22)
|
||||
|
||||
## [1.0.7] - 2026-03-06
|
||||
### Changed
|
||||
- chore(deps): update docker/build-push-action action to v7 (#19)
|
||||
|
||||
## [1.0.6] - 2026-03-05
|
||||
### Changed
|
||||
- fix docker login during push stage (#18)
|
||||
|
||||
## [1.0.5] - 2026-03-05
|
||||
### Changed
|
||||
- fix deploy pipeline stages for token registry uploads (#17)
|
||||
|
||||
## [1.0.4] - 2026-03-04
|
||||
### Changed
|
||||
- fix deploy pipeline (#15)
|
||||
|
||||
## [1.0.3] - 2026-02-23
|
||||
### Changed
|
||||
- fix(deps): update dependency vue-router to v5 (#12)
|
||||
|
||||
## [1.0.2] - 2026-02-22
|
||||
### Changed
|
||||
- chore(deps): update dependency eslint to v10 (#10)
|
||||
|
||||
## [1.0.1] - 2026-02-22
|
||||
### Changed
|
||||
- chore: Configure Renovate (#7)
|
||||
|
||||
@@ -2,5 +2,5 @@ apiVersion: v2
|
||||
name: kestrelos
|
||||
description: KestrelOS TOC for OSINT feeds - map, camera feeds, offline tiles
|
||||
type: application
|
||||
version: 1.0.1
|
||||
appVersion: "1.0.1"
|
||||
version: 1.1.2
|
||||
appVersion: "1.1.2"
|
||||
|
||||
@@ -2,7 +2,7 @@ replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: git.keligrubb.com/keligrubb/kestrelos
|
||||
tag: 1.0.1
|
||||
tag: 1.1.2
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
service:
|
||||
|
||||
Generated
+2780
-4024
File diff suppressed because it is too large
Load Diff
+3
-7
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kestrelos",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.2",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -31,9 +31,8 @@
|
||||
"nuxt": "^4.0.0",
|
||||
"openid-client": "^6.8.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"sqlite3": "^5.1.7",
|
||||
"vue": "^3.4.0",
|
||||
"vue-router": "^4.4.0",
|
||||
"vue-router": "^5.0.0",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -43,11 +42,8 @@
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@vitest/coverage-v8": "^4.0.0",
|
||||
"@vue/test-utils": "^2.4.0",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint": "^10.0.0",
|
||||
"happy-dom": "^20.6.1",
|
||||
"vitest": "^4.0.0"
|
||||
},
|
||||
"overrides": {
|
||||
"tar": "^7.5.7"
|
||||
}
|
||||
}
|
||||
|
||||
+42
-9
@@ -3,18 +3,51 @@ set -e
|
||||
|
||||
# version
|
||||
msg="${CI_COMMIT_MESSAGE:-}"
|
||||
# optional PR body (written by workflow from Gitea API when this commit is a merged PR)
|
||||
if [ -f .ci_pr_body ]; then
|
||||
CI_PR_DESCRIPTION=$(cat .ci_pr_body); rm -f .ci_pr_body
|
||||
else
|
||||
CI_PR_DESCRIPTION=""
|
||||
fi
|
||||
export CI_PR_DESCRIPTION
|
||||
bump=patch
|
||||
echo "$msg" | grep -qi minor: && bump=minor
|
||||
# Conventional commits: chore/fix => patch, feat => minor
|
||||
echo "$msg" | grep -Eqi '(^|[[:space:]])(fix|chore)(\([^)]*\))?:' && bump=patch
|
||||
echo "$msg" | grep -Eqi '(^|[[:space:]])feat(\([^)]*\))?:' && bump=minor
|
||||
# Conventional commits breaking change: type!:
|
||||
echo "$msg" | grep -Eqi '(^|[[:space:]])[a-zA-Z]+(\([^)]*\))?!:' && bump=major
|
||||
# Explicit bump prefixes still supported (but never downgrade a major bump)
|
||||
echo "$msg" | grep -qi minor: && [ "$bump" != "major" ] && bump=minor
|
||||
echo "$msg" | grep -qi major: && bump=major
|
||||
cur=$(awk '/"version"/ { match($0, /[0-9]+\.[0-9]+\.[0-9]+/); print substr($0, RSTART, RLENGTH); exit }' package.json)
|
||||
case "$cur" in
|
||||
[0-9]*.[0-9]*.[0-9]*) ;;
|
||||
*) echo "error: package.json version must be x.y.z (got: $cur)"; exit 1 ;;
|
||||
esac
|
||||
major=$(echo "$cur" | cut -d. -f1); minor=$(echo "$cur" | cut -d. -f2); patch=$(echo "$cur" | cut -d. -f3)
|
||||
case "$bump" in major) major=$((major+1)); minor=0; patch=0 ;; minor) minor=$((minor+1)); patch=0 ;; patch) patch=$((patch+1)) ;; esac
|
||||
newVersion="$major.$minor.$patch"
|
||||
[ -z "$cur" ] && { echo "error: could not read version from package.json"; exit 1; }
|
||||
|
||||
# changelog entry (strip prefix from first line)
|
||||
changelogEntry=$(echo "$msg" | head -1 | awk '{sub(/^[mM]ajor:[ \t]*/,""); sub(/^[mM]inor:[ \t]*/,""); sub(/^[pP]atch:[ \t]*/,""); print}')
|
||||
url="https://${CI_REPO_OWNER}:${GITEA_REPO_TOKEN}@${CI_FORGE_URL#https://}/${CI_REPO_OWNER}/${CI_REPO_NAME}.git"
|
||||
if [ -n "$(git ls-remote "$url" "refs/tags/v$newVersion" 2>/dev/null)" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# changelog entry (strip explicit bump prefixes & any conventional-commit type(scope):); optional PR description enriches it
|
||||
changelogEntry=$(
|
||||
echo "$msg" \
|
||||
| head -1 \
|
||||
| sed -E 's/^[[:space:]]*[mM]ajor:[[:space:]]*//; s/^[[:space:]]*[mM]inor:[[:space:]]*//; s/^[[:space:]]*[pP]atch:[[:space:]]*//' \
|
||||
| sed -E 's/^[[:space:]]*[a-zA-Z]+(\([^)]*\))?:[[:space:]]*//'
|
||||
)
|
||||
[ -z "$changelogEntry" ] && changelogEntry="Release v$newVersion"
|
||||
if [ -n "$CI_PR_DESCRIPTION" ]; then
|
||||
changelogFull="- $changelogEntry
|
||||
|
||||
$CI_PR_DESCRIPTION"
|
||||
else
|
||||
changelogFull="- $changelogEntry"
|
||||
fi
|
||||
|
||||
# bump files
|
||||
awk -v v="$newVersion" '/"version"/ { sub(/[0-9]+\.[0-9]+\.[0-9]+/, v) } { print }' package.json > package.json.tmp && mv package.json.tmp package.json
|
||||
@@ -24,18 +57,18 @@ awk -v v="$newVersion" '/^ tag:/ { $0 = " tag: " v }; { print }' helm/kestrelo
|
||||
# changelog
|
||||
new="## [$newVersion] - $(date +%Y-%m-%d)
|
||||
### Changed
|
||||
- $changelogEntry
|
||||
$changelogFull
|
||||
|
||||
"
|
||||
{ [ ! -f CHANGELOG.md ] && printf '# Changelog\n\n'; printf '%s' "$new"; [ -f CHANGELOG.md ] && cat CHANGELOG.md; } > CHANGELOG.md.tmp && mv CHANGELOG.md.tmp CHANGELOG.md
|
||||
# Create CHANGELOG.md if missing (first release); otherwise prepend new entry to existing content.
|
||||
{ [ ! -f CHANGELOG.md ] && printf '# Changelog\n\n'; printf '%s' "$new"; [ -f CHANGELOG.md ] && cat CHANGELOG.md || true; } > CHANGELOG.md.tmp && mv CHANGELOG.md.tmp CHANGELOG.md
|
||||
|
||||
# git
|
||||
git config user.email "ci@kestrelos" && git config user.name "CI"
|
||||
git add package.json helm/kestrelos/Chart.yaml helm/kestrelos/values.yaml CHANGELOG.md
|
||||
git commit -m "release v$newVersion [skip ci]"
|
||||
url="https://${CI_REPO_OWNER}:${GITEA_REPO_TOKEN}@${CI_FORGE_URL#https://}/${CI_REPO_OWNER}/${CI_REPO_NAME}.git"
|
||||
git tag "v$newVersion"
|
||||
# artifact for kaniko (tag list)
|
||||
# artifact for docker (tag list)
|
||||
printf '%s\n%s\n' "$newVersion" "latest" > .tags
|
||||
retry() { n=0; while ! "$@"; do n=$((n+1)); [ $n -ge 3 ] && return 1; sleep 2; done; }
|
||||
retry git push "$url" HEAD:main "v$newVersion"
|
||||
@@ -43,7 +76,7 @@ retry git push "$url" HEAD:main "v$newVersion"
|
||||
# gitea release
|
||||
body="## Changelog
|
||||
### Changed
|
||||
- $changelogEntry
|
||||
$changelogFull
|
||||
|
||||
## Installation
|
||||
- [Docker image](${CI_FORGE_URL}/${CI_REPO_OWNER}/-/packages/container/${CI_REPO_NAME})
|
||||
|
||||
+86
-24
@@ -1,14 +1,12 @@
|
||||
import { join, dirname } from 'node:path'
|
||||
import { mkdirSync, existsSync } from 'node:fs'
|
||||
import { randomBytes, randomUUID } from 'node:crypto'
|
||||
import { createRequire } from 'node:module'
|
||||
import { promisify } from 'node:util'
|
||||
import { randomBytes } from 'node:crypto'
|
||||
import { hashPassword } from './password.js'
|
||||
import { registerCleanup } from './shutdown.js'
|
||||
|
||||
// Resolve from project root so bundled server (e.g. .output) finds node_modules/sqlite3
|
||||
const requireFromRoot = createRequire(join(process.cwd(), 'package.json'))
|
||||
const sqlite3 = requireFromRoot('sqlite3')
|
||||
const { DatabaseSync } = requireFromRoot('node:sqlite')
|
||||
|
||||
const SCHEMA_VERSION = 4
|
||||
const DB_BUSY_TIMEOUT_MS = 5000
|
||||
@@ -144,7 +142,6 @@ const initDb = async (db, run, all, get) => {
|
||||
catch {
|
||||
// WAL not supported (e.g., network filesystem)
|
||||
}
|
||||
db.configure('busyTimeout', DB_BUSY_TIMEOUT_MS)
|
||||
|
||||
await run(SCHEMA.schema_version)
|
||||
await run(SCHEMA.users)
|
||||
@@ -168,7 +165,7 @@ const initDb = async (db, run, all, get) => {
|
||||
|
||||
await run(
|
||||
'INSERT INTO users (id, identifier, password_hash, role, created_at, auth_provider, oidc_issuer, oidc_sub) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
[crypto.randomUUID(), identifier, hashPassword(plainPassword), 'admin', new Date().toISOString(), 'local', null, null],
|
||||
[randomUUID(), identifier, hashPassword(plainPassword), 'admin', new Date().toISOString(), 'local', null, null],
|
||||
)
|
||||
|
||||
if (!email || !password) {
|
||||
@@ -181,37 +178,104 @@ const initDb = async (db, run, all, get) => {
|
||||
export async function getDb() {
|
||||
if (dbInstance) return dbInstance
|
||||
|
||||
const db = new sqlite3.Database(getDbPath(), (err) => {
|
||||
if (err) {
|
||||
const sqliteDb = (() => {
|
||||
try {
|
||||
return new DatabaseSync(getDbPath(), { timeout: DB_BUSY_TIMEOUT_MS })
|
||||
}
|
||||
catch (err) {
|
||||
console.error('[db] Failed to open database:', err.message)
|
||||
throw err
|
||||
}
|
||||
})
|
||||
})()
|
||||
|
||||
const run = promisify(db.run.bind(db))
|
||||
const all = promisify(db.all.bind(db))
|
||||
const get = promisify(db.get.bind(db))
|
||||
const run = (sql, params = []) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const stmt = sqliteDb.prepare(sql)
|
||||
try {
|
||||
stmt.run(...params)
|
||||
resolve()
|
||||
}
|
||||
catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
stmt.finalize()
|
||||
}
|
||||
catch {
|
||||
// ignore finalize errors
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const all = (sql, params = []) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const stmt = sqliteDb.prepare(sql)
|
||||
try {
|
||||
const rows = stmt.all(...params)
|
||||
resolve(rows)
|
||||
}
|
||||
catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
stmt.finalize()
|
||||
}
|
||||
catch {
|
||||
// ignore finalize errors
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const get = (sql, params = []) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const stmt = sqliteDb.prepare(sql)
|
||||
try {
|
||||
const row = stmt.get(...params)
|
||||
resolve(row)
|
||||
}
|
||||
catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
stmt.finalize()
|
||||
}
|
||||
catch {
|
||||
// ignore finalize errors
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const wrappedDb = {
|
||||
close(callback) {
|
||||
try {
|
||||
sqliteDb.close()
|
||||
if (typeof callback === 'function') callback(null)
|
||||
}
|
||||
catch (error) {
|
||||
if (typeof callback === 'function') callback(error)
|
||||
else throw error
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
await initDb(db, run, all, get)
|
||||
await initDb(wrappedDb, run, all, get)
|
||||
}
|
||||
catch (error) {
|
||||
db.close()
|
||||
wrappedDb.close()
|
||||
console.error('[db] Database initialization failed:', error.message)
|
||||
throw error
|
||||
}
|
||||
|
||||
dbInstance = { db, run, all, get }
|
||||
dbInstance = { db: wrappedDb, run, all, get }
|
||||
|
||||
registerCleanup(async () => {
|
||||
if (dbInstance) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
dbInstance.db.close((err) => {
|
||||
if (err) reject(err)
|
||||
else resolve()
|
||||
})
|
||||
})
|
||||
dbInstance.db.close()
|
||||
}
|
||||
catch (error) {
|
||||
console.error('[db] Error closing database during shutdown:', error?.message)
|
||||
@@ -290,9 +354,7 @@ export async function withTransaction(db, callback) {
|
||||
export function closeDb() {
|
||||
if (!dbInstance) return
|
||||
try {
|
||||
dbInstance.db.close((err) => {
|
||||
if (err) console.error('[db] Error closing database:', err.message)
|
||||
})
|
||||
dbInstance.db.close()
|
||||
}
|
||||
catch (error) {
|
||||
console.error('[db] Error closing database:', error.message)
|
||||
|
||||
Reference in New Issue
Block a user