Commit Graph

392 Commits

Author SHA1 Message Date
renovate[bot] 988dfa0b3a chore(deps): update golangci/golangci-lint-action action to v9.3.0 2026-06-29 17:52:24 +00:00
renovate[bot] b0bbfa677a chore(deps): update playwright to v1.61.1 2026-06-29 08:16:15 +02:00
renovate[bot] a2063a27a8 chore(deps): update actions/ai-inference action to v2.1.1 2026-06-29 08:12:04 +02:00
kolaente 8d0814e460
chore(ci): remove stale label from PR when there is activity 2026-06-28 19:41:30 +02:00
renovate[bot] d374c8e6f9 chore(deps): update actions/checkout action to v7 2026-06-28 09:06:15 +00:00
renovate[bot] dab2ac473f chore(deps): update postgres:18 docker digest to 4aabea7 2026-06-27 19:40:01 +00:00
renovate[bot] ba5c09f962 chore(deps): update actions/cache action to v6 2026-06-27 19:39:18 +00:00
renovate[bot] 07d39b4290 chore(deps): pin dependencies 2026-06-27 18:01:23 +00:00
kolaente 2395239f0b
fix(ci): generate config.yml.sample in release-os-package for vikunja
vikunja's nfpm.yaml packs ./config.yml.sample as /etc/vikunja/config.yml.
The release-binaries action already regenerates it for the zip bundles,
but release-os-package runs on a fresh runner without that file, so
nfpm aborted with "matching ./config.yml.sample: file does not exist"
on every vikunja os-package matrix shard (the veans shards skip this
step entirely).

Add a vikunja-only step to regenerate it before nfpm runs.
2026-05-27 18:16:38 +02:00
kolaente 1e1fcaafbc
fix(ci): drop "./" from PACKAGE_OUTPUT_DIR so strip-path-prefix matches
The s3-action expands the upload glob into paths without a leading
"./", but strip-path-prefix was set to "./dist/os-packages/" (or
"./veans/dist/os-packages/"). The prefix never matched, so packages
landed at /<project>/<version>/<project>/dist/os-packages/<file>
instead of /<project>/<version>/<file>.

Drop the "./" prefix to match the working DIST_PREFIX pattern in
release-binaries.
2026-05-27 17:35:58 +02:00
kolaente 4f8a44de89
fix(ci): switch release composite actions to unstable on non-tag builds
The release-binaries and release-os-package composite actions were
comparing the raw release-version input against the literal "main" to
decide whether to use "unstable" for filenames and the S3 directory.
Callers always pass `steps.ghd.outputs.describe`, which is a value
like `v2.3.0-408-ge053d317` on non-tag builds — so the check never
fired and unstable artifacts landed under `/<project>/<describe>/`
with `<project>-<describe>-...` filenames.

Drive the switch from `github.ref_type == 'tag'` instead, matching the
pattern the desktop and config-yaml jobs in release.yml already use.
The raw describe value still flows into RELEASE_VERSION so the binary
and package metadata keep the precise commit reference.
2026-05-27 17:32:47 +02:00
kolaente e053d3172f
fix(ci): escape ${{ secrets.* }} mention in release-binaries description
GitHub's action manifest parser evaluates `${{ ... }}` expressions inside
`description:` block scalars, and `secrets` isn't a valid context in a
composite action — so the literal example text in the docstring caused
manifest validation to fail before any step ran.
2026-05-27 17:02:56 +02:00
kolaente be7eabb9b3 ci: move build-mage prep job out of test.yml into release.yml
build_mage_bin is only consumed by publish-repos in release.yml, so it
doesn't belong in the test workflow. Move it to release.yml as a
prep job and add it to publish-repos's needs list.
2026-05-27 13:01:44 +00:00
kolaente ed9df9064c refactor(ci): derive composite-action inputs from project name
Reviewer asked us to stop over-configuring the release-binaries and
release-os-package composite actions — they're called only with
vikunja or veans, so per-project paths, artifact names, cache keys, S3
target, and version-or-unstable can all be derived inside the action
from the project name. The xgo-out-name input goes away too.

Vikunja-specific pre-build (downloading frontend_dist, generating
config.yml.sample) now happens inside the action, gated on the project
input. Callers no longer need those preamble steps.

Secrets stay as inputs — composite actions can't read \`\${{ secrets.* }}\`
directly; passing them through is the simplest workaround.

Each callsite shrinks to ~13 lines of mostly-secret pass-through plus
2-4 lines of real parameters.
2026-05-27 13:01:44 +00:00
kolaente e903b72b9e refactor(ci): call release composite actions from release.yml
Replace the inline bodies of binaries, veans-binaries, os-package, and
veans-os-package jobs with calls to the new release-binaries and
release-os-package composite actions. Each call site is now ~25 lines
of inputs instead of ~75 lines of duplicated mage+upx+gpg+s3 plumbing.

publish-repos switches from the parent's ./mage-static to the
prebuilt build_mage_bin artifact so it can drive build/'s repo metadata
targets inside the publish-repos containers.
2026-05-27 13:01:44 +00:00
kolaente 95f65c4712 ci: prebuild static build/mage for repo metadata containers
publish-repos runs inside ubuntu/fedora/archlinux containers that don't
ship a Go toolchain. Compile build/magefile.go into a static binary in
the test workflow (mirroring the existing mage_bin job for the parent
magefile) and upload it as the build_mage_bin artifact so publish-repos
can chmod+x and run it without setup-go.
2026-05-27 13:01:44 +00:00
kolaente c690f74d75 feat(ci): add release-binaries and release-os-package composite actions
Two reusable composite actions wrap the CI side of the release pipeline:

- release-binaries: setup-go, install mage + upx, cache xgo, invoke
  `mage release:build <project>` from build/, GPG-sign the zip bundles,
  upload to S3, store binaries and zips as workflow artifacts.

- release-os-package: download a binaries artifact, install mage,
  `mage release:prepare-nfpm-config <project> <arch>`, stage the binary,
  nfpm pack (with rpm signing inline and archlinux signing after), upload
  to S3, store the package as an artifact.

Both actions are parameterized on project name, output paths, artifact
names, S3 target, and GPG/S3 secrets — adding a third Go binary to the
monorepo just means defining its project in build/magefile.go and adding
a four-line call site in release.yml.
2026-05-27 13:01:44 +00:00
kolaente f39cf00290 ci: register custom actionlint runner labels
Declare namespace-profile-default and blacksmith-8vcpu-ubuntu-2204 as
known self-hosted runners so actionlint stops flagging them as unknown.
2026-05-27 13:01:44 +00:00
kolaente 5f00fca166 feat(veans): build and publish veans alongside vikunja
Cross-compile veans for the same OS/arch matrix as the main vikunja
binary, wrap each into a signed zip, build deb/rpm/apk/archlinux
packages via nfpm, and merge those into the existing dl.vikunja.io
package repos so `apt install veans` works from the same source.

- veans/magefile.go: Release namespace (xgo cross-compile, upx, sha256,
  per-target zip bundle, nfpm.yaml templating).
- veans/nfpm.yaml: minimal — binary at /usr/local/bin/veans, no service
  or postinstall.
- .github/workflows/release.yml: veans-binaries + veans-os-package
  jobs, veans artifacts merged into publish-repos and create-release.
S3 layout mirrors vikunja under /veans/<version>/.
2026-05-27 13:01:44 +00:00
Tink bot 98affb265a test(veans): cover bootstrap validators and prompt UX
Three helpers I added recently have no e2e coverage because the
suite always passes --bot-username with a valid name and
--yes-buckets to skip prompts.

Nine tests in a new bootstrap_test.go:

- TestValidateBotUsername — table-driven, 18 rows: valid shapes
  (bot-foo, bot-foo-bar, bot-foo123, bot-foo_bar, bot-foo.bar,
  bot-a), invalid prefix (foo, Bot-foo, ""), invalid chars
  (spaces, commas, uppercase, !, embedded space), the reserved
  link-share-N pattern, and the bare "bot-" edge.

- TestConfirmOverwriteExistingConfig — file-missing path, the
  OverwriteExistingConfig=true short-circuit, every interesting
  prompt answer (y, yes, Y, Yes, "  yes  " → proceed; n, "",
  garbage → CodeConflict with path in message; prompter error
  → CodeUnknown wrapping the original via errors.Is).

- TestBootstrapBuckets_{AllPresent,AutoApprove,PromptDeclined,
  PromptAborted,PromptUnknownCap,PromptAccepted} — drive the
  function against a stub httptest server (bucketServer helper)
  that records ListBuckets responses and CreateBucket payloads,
  with a scripted queuePrompter for the prompt-driven cases.
  Covers the alias-match short circuit, the auto-approve path,
  the new declined/aborted/retry-cap paths, and the y-accepted
  path.

Local helpers (queuePrompter for scripted answers with injectable
error; bucketServer for the stubbed bucket endpoints) stay in the
test file — no production code changes.
2026-05-27 08:21:57 +00:00
Tink bot ba6615f378 feat(veans): warn when Chain.Set falls back past a failed backend
A keyring transient failure on Set silently falls through to the file
backend today, which leaves a stale keyring entry from any prior
successful write shadowing the new file-backend token. Fixing the
shadow itself is deferred (would need a Set-and-Delete coordination,
or a stricter contract).

What we can do cheaply: surface the fallback so an operator hitting
the shadow has a breadcrumb. On Chain.Set fallthrough past a writable
backend that errored, print:

  veans: credential store: keyring rejected write (X); falling back to file

The warning goes to stderr (not the structured envelope — Set still
returns nil because the write landed somewhere). Env-backend's
read-only skip is unchanged and silent.

ChainStderr is exposed as a package var so tests can capture/assert
the warning when we backfill credential-store coverage.
2026-05-27 08:21:57 +00:00
Tink bot c4a0575305 feat(veans): offer "create a new project" from init's picker
The project picker used to require at least one pre-existing project
and would otherwise hard-error: "no projects visible to this user —
create one in the Vikunja UI first". Now it always offers an extra
numbered entry "Create a new project" and, when the user picks it,
prompts for a title (required) + identifier (optional). Empty-list
case routes straight to creation.

Backed by a new client.CreateProject(ctx, *Project) method (`PUT
/projects`); the e2e harness now uses that instead of the raw c.Do
call it did before.

Also fixed a latent bufio bug in StdPrompter.ReadLine that this work
surfaced: every call created a fresh bufio.Reader, which read-ahead a
buffer and threw it away on return. Second+ prompts read empty. Reuse
one buffered reader on the StdPrompter instance.
2026-05-27 08:21:57 +00:00
Tink bot 632579b304 ci(veans): add fast veans-test job for unit tests 2026-05-27 08:21:57 +00:00
Tink bot 202a5f60b0 ci(veans): add veans-lint job to Test workflow 2026-05-27 08:21:57 +00:00
Tink bot 35aa486eb5 feat(veans): use OAuth 2.0 Authorization Code + PKCE as default auth
Vikunja's built-in OAuth server (Vikunja 2.3+) does not require client
registration and accepts arbitrary client_ids — it just enforces PKCE
(S256) and constrains redirect URIs to the vikunja- scheme. Earlier I
deferred OAuth on the assumption it needed a registered client; that
was wrong, and the docs make the path much smoother than POST /login.

The custom-scheme constraint (no http:// loopback) is side-stepped by
manual paste-back: veans prints the authorize URL, the user signs in,
their browser fails to open vikunja-veans-cli://callback?code=... and
shows an error, the user copies the URL from the address bar and
pastes it back. CLI extracts code + state, verifies state for CSRF,
exchanges via POST /api/v1/oauth/token (JSON body — Vikunja rejects
form-encoded), and returns the access token.

Resolution order in AcquireHumanToken:
  1. --token (paste-in JWT or personal API token; SSO/OIDC users)
  2. --use-password / --username + --password (POST /login)
  3. OAuth flow (interactive default)

login command supports the same --use-password / --token escape hatches
for token rotation on instances with OAuth disabled.

Includes unit tests for the PKCE generator (verifier shape per RFC 7636,
challenge = SHA256(verifier) base64url-no-pad), authorize-URL
construction, and the lenient callback parser (full URL / query-only /
bare code).
2026-05-27 08:21:57 +00:00
Tink bot 950d41df91 ci(veans): add veans-e2e workflow 2026-05-27 08:21:57 +00:00
Tink bot 52f3dd6806 fix(ci): commit newly added Crowdin translation files
The Crowdin sync workflow used `git diff --quiet` and `git commit -am`,
both of which only consider tracked files. New language files downloaded
by Crowdin (e.g. el-GR, th-TH) were therefore left untracked and silently
dropped on each run.

Switch the change check to `git status --porcelain` scoped to the
translation directories and stage them explicitly before committing so
new locales are included.
2026-05-18 17:57:21 +00:00
kolaente 12f07529e5
chore: update stale workflow 2026-04-29 09:10:09 +02:00
kolaente 1d637a4ac6 refactor(magefile): consolidate api+frontend translation checks into one task
Previously the PR introduced a separate `check:frontendTranslations` mage
task and a second CI job. Merge both into the existing `check:translations`
task and a single CI job. Also rename internal references from "backend" to
"api" to match the project convention (Vikunja's Go server is the api, not
the backend).
2026-04-23 13:30:51 +02:00
kolaente edd83f5e92 ci: run frontend translation check as a hard failure
Add a frontend-check-translations job that runs the new
check:frontendTranslations mage task. Like the existing
api-check-translations job, failures hard-fail CI. This makes
reviewers catch dead keys and missing $t() wiring up front instead of
having to flag them manually in pull request review.
2026-04-23 13:30:51 +02:00
kolaente 0b45cff583
feat(ci): sign archlinux packages with GPG for pacman verification
Pacman verifies individual package signatures (.sig files). Add GPG
setup and detach-sign step for archlinux packages in the os-package
job. The .sig is uploaded alongside the package to S3.
2026-04-14 19:35:23 +02:00
kolaente c970f87e89
fix(ci): resolve symlinks before upload instead of deleting them
S3 can't store symlinks. Previously all symlinks were deleted, which
removed vikunja.db -> vikunja.db.tar.gz needed by pacman. Now resolve
symlinks into real file copies first, then delete package files.
2026-04-14 17:08:27 +02:00
kolaente fdeacd3eaf
fix(ci): write GPG key directly to file instead of importing and exporting
No need to import the key into GPG and then export it again. Just
write the secret key data directly to a file for nfpm to use.
2026-04-14 16:22:52 +02:00
kolaente f6ec5d8e96
feat(ci): sign RPM packages with GPG via nfpm
Add GPG signing configuration to nfpm.yaml for rpm packages. The
os-package job now sets up GPG and exports the key for nfpm to use
during package creation.
2026-04-14 15:47:31 +02:00
kolaente 4d8c37f8ff
fix(ci): exclude package files from repo metadata upload
The dl.vikunja.io worker redirects package file requests under /repos/
to the existing artifacts, so uploading them again is redundant.
Remove .deb, .rpm, .apk, .archlinux, .pacman, .pkg.tar.zst files
and symlinks before uploading to R2.
2026-04-14 14:36:00 +02:00
kolaente 77167eb356
fix(ci): pin s3-action to main branch 2026-04-14 12:47:18 +02:00
kolaente 3d08a4f78f
fix(ci): update s3-action to skip directories during upload
The s3-action glob matched directories causing EISDIR errors. Fixed
the action to filter with fs.statSync().isFile(). Updated all
s3-action references to the new version.
2026-04-14 12:25:43 +02:00
kolaente 797c813091
fix(ci): revert S3 glob to **/* and clean up directories before upload
The **/*.* glob skipped extensionless files like Release, InRelease,
and Packages. Revert to **/* and instead remove reprepro's internal
db/conf directories and directory symlinks before uploading.
2026-04-14 10:02:50 +02:00
kolaente cff690fb5f
fix(ci): skip desktop .apk in APK repo, not a valid Alpine package
The desktop .apk file from electron-builder is not an Alpine APK
package and causes apk index to fail with "v2 package format error".
2026-04-13 16:15:41 +02:00
kolaente a110642093
fix(ci): inline APK repo generation to avoid glibc binary on Alpine
The mage-static binary is compiled with glibc which can't run on
Alpine's musl. Instead of fighting compatibility, inline the APK
repo generation as shell commands since the logic is simple.
2026-04-13 15:18:08 +02:00
kolaente 29098aee62
fix(ci): add libc6-compat for Alpine, fix S3 upload glob, add debug
- Install libc6-compat on Alpine so the glibc-linked mage binary runs
- Change S3 upload glob from **/* to **/*.* to skip directories
- Add debug step to inspect mage-static binary on Alpine
2026-04-13 12:13:05 +02:00
kolaente f3aaf27242
fix(ci): set RELEASE_VERSION to avoid git dependency in containers
The mage initVars calls git describe when RELEASE_VERSION is not
set. Setting it avoids needing git in the container images.
2026-04-13 11:45:37 +02:00
kolaente cd61db4415
refactor(ci): split publish-repos into matrix with native containers
Each package format now runs in its native container image:
- apt: ubuntu:noble (reprepro)
- rpm: fedora:latest (createrepo_c)
- pacman: archlinux:latest (repo-add + bsdtar built-in)
- apk: alpine:latest (apk + abuild-sign built-in)

This eliminates cross-distro tool availability issues. Desktop
packages are downloaded and renamed per format to match the mage
target glob patterns. Also adds --allow-untrusted to apk index
since nfpm-produced .apk packages are unsigned.
2026-04-13 11:19:12 +02:00
kolaente 136fafdf37
fix(ci): install libarchive-tools for repo-add bsdtar dependency
repo-add uses bsdtar to validate packages, which requires
libarchive-tools. The .archlinux extension works fine with repo-add
so the rename to .pkg.tar.zst was unnecessary. Also removes debug
steps.
2026-04-13 10:50:56 +02:00
kolaente 2a2b3c787e
fix(ci): add deeper debug for archlinux/pacman package contents 2026-04-13 08:22:18 +02:00
kolaente ab6cdf91eb
fix(ci): add debug step to list incoming package files 2026-04-12 19:56:03 +02:00
kolaente e1fed9e252
fix(ci): install makepkg for repo-add utility scripts
repo-add from pacman-package-manager sources scripts from
/usr/share/makepkg/util/ which are shipped in the separate
makepkg package on Ubuntu.
2026-04-12 17:59:32 +02:00
kolaente 80ecaeb567
fix(ci): sign APT Release files manually instead of via reprepro gpgme
reprepro uses gpgme for signing which fails in CI environments because
gpgme cannot access pinentry. Instead, remove SignWith from the reprepro
distributions config and sign Release files manually with gpg after
reprepro finishes, producing both Release.gpg and InRelease.
2026-04-12 17:32:11 +02:00
kolaente e8d12186d1
fix(ci): configure gpg loopback pinentry for reprepro signing
reprepro uses gpgme which bypasses the preset passphrase cache and
tries to launch a pinentry dialog, failing in CI with
"Inappropriate ioctl for device". Adding loopback pinentry mode
allows gpgme to obtain the passphrase without a dialog.
2026-04-12 16:49:36 +02:00
kolaente b375399e34 feat(ci): add publish-repos job for OS package repository metadata
New CI job runs after os-package and desktop jobs complete. Downloads
all package artifacts, runs Mage repo targets to generate repository
metadata (APT, RPM, APK, Pacman), GPG-signs the metadata, and uploads
to R2 under /repos/.

Publishes to stable suite for tagged releases, unstable for main
branch builds. Artifact uploads from os-package and desktop jobs are
no longer gated on tags to support this.
2026-04-12 12:06:14 +00:00