Update Loops to security release beta.15
This commit is contained in:
1 parent
3795736e5e
commit
a6ffa46d42
9 files changed
+154
-25
No files matched your search
@@ -19,8 +19,36 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Derive immutable chart version
|
||||
run: echo "CHART_VERSION=$(./scripts/chart-version.sh)" >> "$GITHUB_ENV"
|
||||
- name: Derive release versions
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "CHART_VERSION=$(./scripts/chart-version.sh)" >> "$GITHUB_ENV"
|
||||
echo "APP_VERSION=$(sed -n 's/^appVersion: \"\(.*\)\"$/\1/p' chart/Chart.yaml)" >> "$GITHUB_ENV"
|
||||
- name: Build, test and publish Loops image
|
||||
env:
|
||||
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
|
||||
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
|
||||
COMMIT_SHA: ${{ gitea.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -n "$QUAY_USERNAME" && test -n "$QUAY_TOKEN"
|
||||
source_dir="$RUNNER_TEMP/loops-source"
|
||||
git clone --depth 1 --branch "$APP_VERSION" https://github.com/joinloops/loops-server.git "$source_dir"
|
||||
git -C "$source_dir" apply "$GITHUB_WORKSPACE/patches/loops-frontend.patch"
|
||||
docker build --pull \
|
||||
--label "org.opencontainers.image.revision=$COMMIT_SHA" \
|
||||
--label "org.opencontainers.image.version=$APP_VERSION" \
|
||||
--label "org.opencontainers.image.source=https://github.com/joinloops/loops-server" \
|
||||
--tag "registry.brunner.ninja/feedc0de/loops:$APP_VERSION" \
|
||||
"$source_dir"
|
||||
docker run --rm --entrypoint php "registry.brunner.ninja/feedc0de/loops:$APP_VERSION" -r '
|
||||
if (PHP_VERSION_ID < 80512) { fwrite(STDERR, "PHP security floor 8.5.12 not met\\n"); exit(1); }
|
||||
echo PHP_VERSION, "\n";
|
||||
'
|
||||
docker run --rm --entrypoint ffmpeg "registry.brunner.ninja/feedc0de/loops:$APP_VERSION" -version | head -n 1 | grep -F 'ffmpeg version 9.0.2'
|
||||
docker run --rm --entrypoint sh "registry.brunner.ninja/feedc0de/loops:$APP_VERSION" -ec 'grep -R -l "recommended-feed" /var/www/html/public/build/assets >/dev/null; grep -R -l "fyfeed" /var/www/html/public/build/assets >/dev/null'
|
||||
printf '%s' "$QUAY_TOKEN" | docker login registry.brunner.ninja --username "$QUAY_USERNAME" --password-stdin
|
||||
docker push "registry.brunner.ninja/feedc0de/loops:$APP_VERSION"
|
||||
- name: Package and upload
|
||||
env:
|
||||
PACKAGE_USERNAME: ${{ secrets.PACKAGE_USERNAME }}
|
||||
|
||||
@@ -17,4 +17,8 @@ helm repo update brunner.ninja
|
||||
helm upgrade --install loops brunner.ninja/loops --namespace loops --create-namespace --wait --timeout 20m
|
||||
```
|
||||
|
||||
The deployed image is built from Loops source commit f614b34f. The chart's frontend, backend and Following-feed overlays are tied to that exact image; review and rebase them before changing `appVersion` or `image.tag`. The upstream project does not issue SemVer releases for this commit, so Git tag `f614b34f` maps reversibly to chart version `0.0.0-f614b34f`; chart-only commits append their Git-describe count and hash.
|
||||
The chart and image are released together. The Git tag and image tag match the upstream Loops release (for example `v1.0.0-beta.15`), `Chart.yaml.appVersion` is that exact tag, and chart versions come from `git describe --tags --long`. A chart-only commit after a release tag therefore gets a unique version such as `1.0.0-beta.15-1-g<hash>`.
|
||||
|
||||
CI checks out the matching upstream release, applies `patches/loops-frontend.patch`, builds the upstream Dockerfile, verifies PHP 8.5.12+ and FFmpeg 9.0.2, publishes the image to `registry.brunner.ninja`, publishes the chart to the public `brunner.ninja/charts` repository, and promotes the chart version to `homelab-gitops`. The frontend fix is now built into the immutable image; it is not a hash-specific ConfigMap overlay. The backend and Following-feed patches remain chart-managed and must be reviewed against the next upstream release before updating `appVersion`.
|
||||
|
||||
Argo CD runs `php artisan migrate --force` as a sync hook after the database and configuration resources are ready, and before the application Deployments roll out. Application pods keep automatic migrations disabled to avoid concurrent migration runs.
|
||||
+2
-2
@@ -2,5 +2,5 @@ apiVersion: v2
|
||||
name: loops
|
||||
description: Kubernetes deployment contract for loops
|
||||
type: application
|
||||
version: 0.0.0-f614b34f
|
||||
appVersion: "f614b34f"
|
||||
version: 1.0.0-beta.15
|
||||
appVersion: "v1.0.0-beta.15"
|
||||
@@ -109,6 +109,8 @@ kind: Deployment
|
||||
metadata:
|
||||
name: loops-web
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
labels:
|
||||
app.kubernetes.io/name: loops-web
|
||||
app.kubernetes.io/part-of: loops
|
||||
@@ -203,10 +205,6 @@ spec:
|
||||
- name: filesystem-config
|
||||
mountPath: /var/www/html/config/filesystems.php
|
||||
subPath: filesystems.php
|
||||
- name: frontend-hotfix
|
||||
mountPath: /var/www/html/public/build/assets/index-Cng6wq5r.js
|
||||
subPath: index-Cng6wq5r.js
|
||||
readOnly: true
|
||||
- name: backend-hotfix
|
||||
mountPath: /var/www/html/app/Services/ForYouFeedService.php
|
||||
subPath: ForYouFeedService.php
|
||||
@@ -238,9 +236,6 @@ spec:
|
||||
- name: filesystem-config
|
||||
configMap:
|
||||
name: loops-filesystem-config
|
||||
- name: frontend-hotfix
|
||||
configMap:
|
||||
name: loops-frontend-hotfix
|
||||
- name: backend-hotfix
|
||||
configMap:
|
||||
name: loops-backend-hotfix
|
||||
@@ -270,6 +265,8 @@ kind: Deployment
|
||||
metadata:
|
||||
name: loops-horizon
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
labels:
|
||||
app.kubernetes.io/name: loops-horizon
|
||||
app.kubernetes.io/part-of: loops
|
||||
@@ -343,6 +340,8 @@ kind: Deployment
|
||||
metadata:
|
||||
name: loops-transcoder
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
labels:
|
||||
app.kubernetes.io/name: loops-transcoder
|
||||
app.kubernetes.io/component: transcoder
|
||||
@@ -466,6 +465,8 @@ kind: Deployment
|
||||
metadata:
|
||||
name: loops-scheduler
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
labels:
|
||||
app.kubernetes.io/name: loops-scheduler
|
||||
app.kubernetes.io/part-of: loops
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
apiVersion: v1
|
||||
data:
|
||||
index-Cng6wq5r.js: |
|
||||
import{_ as h}from"./HideCommentConfirmModal-QPWnMLxt.js";import{u as k,b as y,a as g,S as v}from"./LoopLink.vue_vue_type_script_setup_true_lang-D1dGRmzo.js";import{u as x}from"./usePublicFeed-BqqecCdM.js";import{c as P}from"./app--YWhAMOJ.js";import{V}from"./VideoPlayer-DP4y4yM4.js";import{y as b,d as F,w as I,a as S,c as C,e as r,u as w,q,ay as B}from"./vendor-core-C7C5leYv.js";import"./Comments-ByHWOGrH.js";import"./KlipyPicker-CbkaMsji.js";import"./comments-CRjqfgs4.js";import"./useHashids-DT1SW4G7.js";import"./AutolinkedText-BAfi_gt-.js";import"./CommentMediaAttachment-DfTLtvaG.js";import"./__vite-browser-external-ke9ZaMpT.js";/* empty css *//* empty css */import"./ShareModal-Bb3hVgNV.js";import"./DmQuickShare-C1LTf34j.js";import"./DmShareModal-DB3TK55A.js";const H=()=>k({queryKey:["fyfeed"],queryFn:({pageParam:a})=>y({cursor:a}),getNextPageParam:a=>a.meta?.next_cursor??void 0,initialPageParam:null,enabled:!0,staleTime:0,retry:2}),X={__name:"index",setup(a){const s=S("authStore"),n=H(),j=x(),{hasInteracted:o,handleFirstInteraction:m,globalMuted:c}=g(),u=(e,t)=>({"video-id":e.id,"video-url":e.media.src_url,"share-url":e.url,"profile-id":e.account.id,username:e.account.username,"profile-image":e.account.avatar,caption:e.caption,hashtags:e.tags,mentions:e.mentions,likes:e.likes,hasLiked:e.has_liked,hasBookmarked:e.has_bookmarked,bookmarks:e.bookmarks,shares:e.shares,comments:[],canComment:e.permissions?.can_comment,"comment-count":e.comments,index:t,isSensitive:e?.is_sensitive,altText:e?.media.alt_text,autoPlay:o.value,muted:c.value}),l=e=>e.id,_=e=>{},f=e=>{},p=()=>{m()};return(e,t)=>{const i=h;return C(),F(P,null,{default:I(()=>[r(v,{key:s.authenticated?"recommended-feed":"public-feed","feed-data":s.authenticated?n:j,"item-component":V,"get-item-props":u,"get-item-key":l,"auto-play":w(o),"scroll-threshold":1.5,"snap-sensitivity":50,onItemVisible:_,onItemHidden:f,onInteraction:p},null,8,["feed-data","auto-play"]),r(i)]),_:1})}}};export{X as default};
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/part-of: loops
|
||||
name: loops-frontend-hotfix
|
||||
namespace: {{ .Release.Namespace }}
|
||||
@@ -3,6 +3,8 @@ kind: Service
|
||||
metadata:
|
||||
name: mysql
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "-1"
|
||||
labels:
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/part-of: loops
|
||||
@@ -21,6 +23,8 @@ kind: StatefulSet
|
||||
metadata:
|
||||
name: mysql
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "-1"
|
||||
labels:
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/part-of: loops
|
||||
@@ -111,6 +115,8 @@ kind: Service
|
||||
metadata:
|
||||
name: valkey
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "-1"
|
||||
labels:
|
||||
app.kubernetes.io/name: valkey
|
||||
app.kubernetes.io/part-of: loops
|
||||
@@ -129,6 +135,8 @@ kind: StatefulSet
|
||||
metadata:
|
||||
name: valkey
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "-1"
|
||||
labels:
|
||||
app.kubernetes.io/name: valkey
|
||||
app.kubernetes.io/part-of: loops
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: loops-migrate
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
app.kubernetes.io/name: loops-migrate
|
||||
app.kubernetes.io/part-of: loops
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: Sync
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded
|
||||
spec:
|
||||
backoffLimit: 2
|
||||
activeDeadlineSeconds: 900
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: loops-migrate
|
||||
app.kubernetes.io/part-of: loops
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: migrate
|
||||
image: {{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }}
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- php
|
||||
- artisan
|
||||
- migrate
|
||||
- --force
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: loops-env
|
||||
- secretRef:
|
||||
name: {{ .Values.runtimeSecret }}
|
||||
@@ -0,0 +1,62 @@
|
||||
diff --git a/resources/js/composables/useFeed.js b/resources/js/composables/useFeed.js
|
||||
--- a/resources/js/composables/useFeed.js
|
||||
+++ b/resources/js/composables/useFeed.js
|
||||
@@ -4,9 +4,9 @@
|
||||
export const useFeed = () => {
|
||||
return useInfiniteQuery({
|
||||
- queryKey: ['feed'],
|
||||
+ queryKey: ['fyfeed'],
|
||||
queryFn: ({ pageParam }) => fetchFeedPage({ cursor: pageParam }),
|
||||
getNextPageParam: (lastPage) => lastPage.meta?.next_cursor ?? undefined,
|
||||
initialPageParam: null,
|
||||
enabled: true,
|
||||
- staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
+ staleTime: 0,
|
||||
retry: 2
|
||||
})
|
||||
}
|
||||
diff --git a/resources/js/pages/index.vue b/resources/js/pages/index.vue
|
||||
--- a/resources/js/pages/index.vue
|
||||
+++ b/resources/js/pages/index.vue
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<FeedLayout>
|
||||
<SnapScrollFeed
|
||||
- key="local-feed"
|
||||
+ :key="authStore.authenticated ? 'recommended-feed' : 'public-feed'"
|
||||
:feed-data="feedData"
|
||||
:item-component="VideoPlayer"
|
||||
:get-item-props="getVideoProps"
|
||||
@@ -18,7 +18,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
-import { inject, computed, shallowRef, watch } from 'vue'
|
||||
+import { inject, computed } from 'vue'
|
||||
import { useFeed } from '~/composables/useFeed'
|
||||
import { usePublicFeed } from '~/composables/usePublicFeed'
|
||||
import { useFeedInteraction } from '~/composables/useFeedInteraction'
|
||||
@@ -28,20 +28,10 @@
|
||||
|
||||
const authStore = inject('authStore')
|
||||
|
||||
-const activeFeed = shallowRef(null)
|
||||
-
|
||||
const { hasInteracted, handleFirstInteraction, globalMuted } = useFeedInteraction()
|
||||
|
||||
-watch(
|
||||
- () => authStore.authenticated,
|
||||
- (isAuthenticated, _, onCleanup) => {
|
||||
- activeFeed.value = isAuthenticated ? useFeed() : usePublicFeed()
|
||||
- },
|
||||
- { immediate: true }
|
||||
-)
|
||||
-
|
||||
-const feedData = computed(() => {
|
||||
- return activeFeed.value
|
||||
-})
|
||||
+const authenticatedFeed = useFeed()
|
||||
+const publicFeed = usePublicFeed()
|
||||
+const feedData = computed(() => authStore.authenticated ? authenticatedFeed : publicFeed)
|
||||
|
||||
const getVideoProps = (post, index) => ({
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
# Loops has no upstream SemVer release. Its source commit is the Git tag.
|
||||
description=$(git describe --tags --long --match 'f[0-9a-f]*' --dirty)
|
||||
# Chart releases follow the upstream Loops application release tag.
|
||||
description=$(git describe --tags --long --match 'v[0-9]*' --dirty)
|
||||
case "$description" in *-dirty) echo 'Refusing dirty tree' >&2; exit 1 ;; esac
|
||||
version=0.0.0-$description
|
||||
version=${description#v}
|
||||
case "$version" in *-0-g*) version=${version%%-0-g*} ;; esac
|
||||
printf '%s\n' "$version"
|
||||
Reference in new issue
Block a user