From c5bf6f8f0d6037a894e0084ca02aabf0f423d752 Mon Sep 17 00:00:00 2001 From: Patrick Niebeling Date: Thu, 13 Aug 2026 12:52:29 +0200 Subject: [PATCH] Add rolling main-branch build workflow for manual server deploys Publishes the appstore package as a Gitea release asset on every push to main, so a deployable build can be grabbed without cutting a version tag first. --- .gitea/workflows/build-main.yml | 57 +++++++++++++++++++++++++++++++++ CLAUDE.md | 8 ++++- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 .gitea/workflows/build-main.yml diff --git a/.gitea/workflows/build-main.yml b/.gitea/workflows/build-main.yml new file mode 100644 index 0000000..bc9c88b --- /dev/null +++ b/.gitea/workflows/build-main.yml @@ -0,0 +1,57 @@ +name: Build main artifact + +on: + push: + branches: + - main + +jobs: + package: + runs-on: gitea-runner-server03 + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v7 + with: + node-version: '24' + + - name: Install JS dependencies + run: npm install + + - name: Build frontend + run: npm run build + + - name: Package appstore artifact + run: make appstore + + - name: Publish rolling "latest-main" release + env: + GITEA_API: ${{ gitea.server_url }}/api/v1 + REPO: ${{ gitea.repository }} + TOKEN: ${{ secrets.RELEASE_TOKEN }} + SHA: ${{ gitea.sha }} + run: | + set -e + TAG="latest-main" + ASSET="build/artifacts/appstore/workflow_deck_automation.tar.gz" + + # Drop any previous release+tag so the download URL always serves the newest build + curl -s -H "Authorization: token $TOKEN" \ + "$GITEA_API/repos/$REPO/releases/tags/$TAG" -o /tmp/old_release.json + OLD_ID=$(node -e "try{const j=require('/tmp/old_release.json');console.log(j.id||'')}catch(e){console.log('')}") + if [ -n "$OLD_ID" ]; then + curl -s -X DELETE -H "Authorization: token $TOKEN" \ + "$GITEA_API/repos/$REPO/releases/$OLD_ID" + fi + curl -s -X DELETE -H "Authorization: token $TOKEN" \ + "$GITEA_API/repos/$REPO/tags/$TAG" > /dev/null || true + + # Re-create the tag+release pointing at the commit that was just built + curl -s -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" \ + -d "{\"tag_name\":\"$TAG\",\"target_commitish\":\"$SHA\",\"name\":\"Latest main build\",\"body\":\"Automatisch aus $SHA gebaut - nur zum schnellen Testen/Deployen, kein offizielles Release.\",\"prerelease\":true}" \ + "$GITEA_API/repos/$REPO/releases" -o /tmp/new_release.json + NEW_ID=$(node -e "console.log(require('/tmp/new_release.json').id)") + + curl -s -X POST -H "Authorization: token $TOKEN" \ + -F "attachment=@$ASSET" \ + "$GITEA_API/repos/$REPO/releases/$NEW_ID/assets?name=workflow_deck_automation.tar.gz" diff --git a/CLAUDE.md b/CLAUDE.md index 3537e5d..d4c6d96 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,12 +47,18 @@ There is no meaningful way to test the Deck integration itself outside a real Ne 1. Bump `` in `appinfo/info.xml` and `version` in `package.json` (keep them in sync). 2. Commit, push to `main`, confirm the lint/phpunit pipelines are green. -3. `git tag vX.Y.Z && git push origin vX.Y.Z` — this triggers `.gitea/workflows/build-release.yml`, which builds the frontend and runs `make appstore`, producing `build/artifacts/appstore/workflow_deck_automation.tar.gz` as a **workflow artifact** (not an attached Gitea Release asset — that would need an additional API step with a repo token, not currently wired up). +3. `git tag vX.Y.Z && git push origin vX.Y.Z` — this triggers `.gitea/workflows/build-release.yml`, which builds the frontend and runs `make appstore`, producing `build/artifacts/appstore/workflow_deck_automation.tar.gz` as a **workflow artifact**, downloadable from the Gitea Actions run for that tag. **If the tagged build fails and no artifact was ever produced**, fix the issue on `main` and *move* the existing tag to the fixed commit instead of bumping to a new version number (`git tag -d vX.Y.Z && git tag -a vX.Y.Z -m "..." && git push origin :refs/tags/vX.Y.Z && git push origin vX.Y.Z`) — this is how `v0.1.0` was handled through several `build-release.yml` fixes before it first built successfully. Once a version has actually produced a published artifact, don't move its tag anymore; bump normally instead. **`actions/upload-artifact` must stay on `@v3`, not `@v4`+.** This self-hosted Gitea instance identifies as GHES to the official `actions/*` JS actions, which refuse to run `@actions/artifact` v2.0.0+ (used internally by `upload-artifact@v4` and later) against GHES: `GHESNotSupportedError`. `actions/checkout` and `actions/setup-node` don't have this problem and can be kept on their latest majors — only the artifact upload/download actions are affected, because they're the ones that talk to a version-specific backend API rather than just running local commands. +### Rolling build for manual server deploys + +`.gitea/workflows/build-main.yml` builds the same appstore package on every push to `main` (no tag needed) and publishes it as the asset on a rolling pre-release tagged `latest-main` — the release+tag are deleted and recreated each run via the Gitea REST API (`curl`, not an `actions/*` artifact action, so the GHES artifact-API restriction above doesn't apply here), so the download URL for `workflow_deck_automation.tar.gz` on that release always serves the newest `main` build. This exists specifically so a build can be grabbed and copied onto the server (`nextcloud.gnilebein.de`) without cutting a version tag first — it produced `v0.1.0`-shaped raw-repo confusion once already when the app directory was populated by copying the git working tree instead of a built package; this gives a one-click alternative to that mistake. + +This requires a **repo secret named `RELEASE_TOKEN`**, a Gitea Personal Access Token with repo write access (create it under the repo owner's Gitea user settings → Applications, then add it under the repo's Settings → Actions → Secrets). Without that secret, the `curl` calls to the Gitea release API in the job will fail with 401s — the workflow doesn't fall back to anything else. + ## Data flow / architecture map - **UI**: `src/PersonalSettings.vue` (Vue 3, mounted from `templates/settings/personal.php` into the section registered by `lib/Settings/PersonalSection.php` + `lib/Settings/Personal.php`) talks to `lib/Controller/WorkflowController.php` (an `OCSController`) via `src/api.js`, hitting OCS routes declared in `appinfo/routes.php` (`/ocs/v2.php/apps/workflow_deck_automation/api/v1/...`).