Skip to content
Open
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .allowed_signers
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
spam_blackhole@farcloser.world namespaces="git" sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIACDOXbkl7DthgVLTVZr8TNQcyQUX00MAqB3mWHik/vhAAAABHNzaDo= apostasie@farcloser.world
44 changes: 42 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# http://editorconfig.org
# DO NOT EDIT MANUALLY.
# This file is common to all projects and managed by limen.
# Global configuration changes proposals can be discussed on https://github.com/farcloser/limen

# https://editorconfig.org
root = true

[*]
Expand All @@ -9,9 +13,45 @@ insert_final_newline = true
indent_style = space
indent_size = 2

[*.diff]
# --- whitespace-significant formats ---
[*.{diff,patch}]
trim_trailing_whitespace = false

[*.md]
trim_trailing_whitespace = false

# --- Go: tabs, rendered 4 wide (gofmt emits tabs; width is display-only) ---
[*.go]
indent_style = tab
indent_size = 4

# --- task runner / build ---
[{Justfile,justfile,.justfile}]
indent_size = 4

[*.just]
indent_size = 4

[Makefile]
indent_style = tab

# --- data formats (YAML must be spaces, never tabs) ---
[*.{json,jsonc,yaml,yml,toml}]
indent_size = 2

# --- JavaScript / TypeScript (2-space matches Prettier/Biome defaults) ---
[*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}]
indent_size = 2

# --- CSS family ---
[*.{css,scss,sass,less,pcss}]
indent_size = 2

# --- HTML / templates ---
[*.{html,htm,vue,svelte,astro}]
indent_size = 2

# --- Rust: rustfmt is 4-space, 100-col ---
[*.rs]
indent_size = 4
max_line_length = 100
10 changes: 10 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Treat all files as binary, with no git magic updating line endings.
# This produces predictable results in different environments.
#
# Contributors on Windows will need to use a modern version of git
# and editors capable of LF line endings.
#
# See https://github.com/golangci/golangci-lint/issues/580
# See https://github.com/golang/go/blob/master/.gitattributes

* -text
54 changes: 54 additions & 0 deletions .github/actions/setup-aqua/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Setup aqua
description: >
Install aqua (pinned, checksum-verified), put its bin directory on PATH, and authorize the repository's committed policy. aqua itself is the one tool aqua cannot pin. Same pins as limen-install: the installer script is fetched at an exact tag and checksum-verified, and it installs an exact aqua version.
runs:
using: composite
steps:
- name: Install aqua (pinned, checksum-verified)
shell: bash
env:
# Bumped manually, as a pair: Renovate cannot recompute the sha256
# that must change with the installer version.
AQUA_INSTALLER_VERSION: v4.0.2
AQUA_INSTALLER_SHA256: 98b883756cdd0a6807a8c7623404bfc3bc169275ad9064dc23a6e24ad398f43d
# renovate: depName=aquaproj/aqua
AQUA_VERSION: v2.60.1
run: |
# Pin the root explicitly: aqua's own Windows build defaults it to
# %LOCALAPPDATA% (adrg/xdg), while the shell installer and the
# hermetic Justfile PATH compute unix-style ~/.local/share — without
# this, the layers disagree on Windows and nothing resolves. Exported
# to GITHUB_ENV so the recipes' aqua agrees too.
AQUA_ROOT_DIR="${AQUA_ROOT_DIR:-$HOME/.local/share/aquaproj-aqua}"
# On windows the pin must be in NATIVE form: git-bash's $HOME is a
# POSIX-only path (/c/Users/...) that native binaries (aqua,
# aqua-proxy, just) misread as current-drive-relative — aqua would
# link tools under D:\c\... while bash's PATH looks in C:\Users\....
# cygpath -m yields C:/Users/..., which every layer reads correctly
# (git-bash included).
if command -v cygpath >/dev/null 2>&1; then
AQUA_ROOT_DIR="$(cygpath -m "$AQUA_ROOT_DIR")"
fi
export AQUA_ROOT_DIR
echo "AQUA_ROOT_DIR=${AQUA_ROOT_DIR}" >>"$GITHUB_ENV"
tmp="$(mktemp -d)"
curl --proto '=https' --tlsv1.2 -fsSL -o "${tmp}/aqua-installer" \
"https://raw.githubusercontent.com/aquaproj/aqua-installer/${AQUA_INSTALLER_VERSION}/aqua-installer"
# No single digest tool exists everywhere: linux and windows git-bash
# ship coreutils sha256sum, macOS ships perl shasum. (Nothing
# aqua-managed can help here — this checksum guards the aqua
# installer itself.)
if command -v sha256sum >/dev/null 2>&1; then
echo "${AQUA_INSTALLER_SHA256} ${tmp}/aqua-installer" | sha256sum -c -
else
echo "${AQUA_INSTALLER_SHA256} ${tmp}/aqua-installer" | shasum -a 256 -c -
fi
chmod +x "${tmp}/aqua-installer"
"${tmp}/aqua-installer" -v "${AQUA_VERSION}"
rm -rf "${tmp}"
echo "${AQUA_ROOT_DIR}/bin" >>"$GITHUB_PATH"
- name: Authorize the committed aqua policy
# Before aqua will read the repo's local registry, the committed policy
# must be allowed — every caller needs this, whatever it runs next.
shell: bash
run: aqua policy allow aqua-policy.yaml
22 changes: 0 additions & 22 deletions .github/workflows/CI.yml

This file was deleted.

82 changes: 82 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# DO NOT EDIT MANUALLY.
# This workflow is generic — no project-specific content — and is destined to
# become part of the canonical baseline limen distributes.
#
# Design: minimal GitHub glue around the same tooling every developer runs
# locally. The only marketplace action is GitHub's own checkout, pinned by
# commit SHA (a tag can be moved to malicious code; a SHA cannot). Everything
# else is pinned, checksum-verified shell: aqua installs the repo's tools at
# the versions aqua.yaml pins, and `just` runs the exact recipes a laptop
# runs — CI green means the same thing as local green, by construction.
# Deliberately absent: runner egress filtering — the available options are
# third-party actions, which this workflow avoids on principle.
name: ci

on:
push:
branches: [main]
pull_request:

# No default token permissions: each job states what it needs. This workflow
# only ever reads the repository — it cannot write code, releases, or
# packages even if a step is compromised.
permissions: {}

# A superseded run (new push to the same branch/PR) is cancelled, not raced.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

# Every run step is bash on every runner: without this, windows defaults to
# PowerShell, which never sees the git-bash environment setup-aqua prepares
# (the whole matrix is designed to run the recipes under git-bash). Explicit
# bash also means `-eo pipefail` everywhere, which the implicit linux/macos
# default lacks.
defaults:
run:
shell: bash

jobs:
verify:
strategy:
fail-fast: false
matrix:
# Pinned images, not -latest: a runner bump is a reviewed change.
# macOS is not redundant: its /bin/bash is 3.2, the portability floor
# the shared recipes target. arm64 (ubuntu-24.04-arm) and windows
# cover the rest of the supported release matrix — windows runs the
# recipes under git-bash.
# These entries are also the default required-status-check contexts of
# the canonical limen:main ruleset (defaultRequiredChecks in
# internal/github/audit.go) — change the two together, in one release.
os: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, windows-2025]
runs-on: ${{ matrix.os }}
# Generous for the windows leg: those runners are markedly slower, and the
# per-GOOS lint recipes now run three legs each.
timeout-minutes: 45
permissions:
contents: read
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# The token is not left behind in .git/config: nothing in this
# workflow talks to GitHub after checkout.
persist-credentials: false
# Full history and refs: `just lint commits` validates the commit
# range against the PR's base branch, which a shallow clone lacks.
fetch-depth: 0

- name: Install aqua (pinned, checksum-verified)
uses: ./.github/actions/setup-aqua

- name: Install pinned tools
# Link-only: the shims download each tool lazily on first use (and it
# is verified against the committed aqua-checksums.json then), so a
# job only ever pays for the tools its recipes actually run.
run: aqua install --only-link

- name: Lint
run: just lint

- name: Test
run: just test
133 changes: 133 additions & 0 deletions .github/workflows/update-aqua-checksum.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# DO NOT EDIT MANUALLY.
# This workflow is generic — no project-specific content — and is destined to
# become part of the canonical baseline limen distributes.
#
# The other half of Renovate: the bot bumps versions in aqua.yaml but cannot
# do the repo-specific follow-up, so without this workflow every version-bump
# PR would merge half-applied. Two follow-ups, on pushes to Renovate's
# branches, one fix-up commit:
# 1. Regenerate aqua-checksums.json with real aqua — a bumped pin with a
# stale checksum breaks every install.
# 2. Converge the limen baseline: when the branch bumps the farcloser/limen
# pin, the repo's canonical files must move with it — a repo is coherent
# only when the limen that wrote its files is the limen it pins (an old
# baseline checked by the new limen is red, and vice versa). The
# branch's own pinned limen runs `fix`; on branches bumping anything
# else it is a no-op.
#
# This is a WRITE workflow — the hardening is deliberate:
# - `push:` on the branch prefix, never pull_request_target: it runs in the
# repo's own context, on branches only writers (the Renovate app) can
# create.
# - The checkout keeps no credential, and the update steps run with no
# secrets in their environment. Neither goes through `just` — the one
# sanctioned deviation: a write-capable workflow does not execute recipe
# code the branch controls. `aqua update-checksum` only downloads and
# hashes declared artifacts; the converge step executes exactly one
# binary, the checksum-pinned limen release the branch declares.
# - The push step runs only git, with the token scoped to that single step.
# - The branch name reaches the shell via env, never template interpolation
# (script-injection hygiene).
# - No loop: a push made with the default GITHUB_TOKEN triggers no further
# workflows — and the no-change early exit terminates recursion
# regardless.
#
# Known trade of the default token: GitHub suppresses workflow runs for
# commits it pushes, so the PR's CI does not re-run on the checksum commit.
# To get CI on the final state of Renovate PRs, register a GitHub App —
# contents:write only, webhook disabled, installed on the org; no infra, it
# is just an identity — and set the org variable UPDATE_AQUA_CHECKSUM_APP_ID
# plus the org secret UPDATE_AQUA_CHECKSUM_APP_PRIVATE_KEY: the workflow
# then mints a one-hour, this-repo-only token per run, so there is no
# long-lived broad credential and nothing that expires on a calendar. A
# fine-grained PAT with contents:write as UPDATE_AQUA_CHECKSUM_TOKEN is the
# drop-in alternative; the token preference order is App, PAT, default.
name: update-aqua-checksum

on:
push:
branches: ["renovate/**"]

permissions: {}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

# Every run step is bash, explicitly: runner-OS shell defaults are a trap
# (windows defaults to PowerShell), and explicit bash adds `-eo pipefail`.
# Uniform across all canonical workflows so adding a runner never changes
# what the steps mean.
defaults:
run:
shell: bash

jobs:
update:
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false

- name: Install aqua (pinned, checksum-verified)
uses: ./.github/actions/setup-aqua

- name: Regenerate aqua-checksums.json
# No tool install: update-checksum only reads the manifest and hashes
# upstream artifacts — the smallest possible surface for a write job.
# Runs first: it is what makes the branch's limen pin installable for
# the converge step below.
run: aqua update-checksum --prune

- name: Converge the limen baseline
# `aqua exec` resolves the branch's own farcloser/limen pin (checksums
# fresh from the step above), downloads it verified, and `limen fix`
# rewrites whatever the new baseline moved. A released limen leaves
# its own pin alone (it already matches), so this cannot ping-pong.
# Skipped in the limen repository itself: there the working tree IS
# the next baseline (the recipes run it via LIMEN_BIN), and a released
# limen "converging" it would revert in-flight baseline work.
if: github.repository != 'farcloser/limen'
run: aqua exec -- limen fix .

- name: Mint a push token, if the App is configured
# Placed after the update steps so those still run with no secrets in
# their environment. The minted token lives one hour and is scoped to
# this repository only; the long-lived private key exists solely to
# mint and never authorizes a push itself. Guarded on the variable so
# repos without the App fall through to the token chain below.
if: vars.UPDATE_AQUA_CHECKSUM_APP_ID != ''
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ vars.UPDATE_AQUA_CHECKSUM_APP_ID }}
private-key: ${{ secrets.UPDATE_AQUA_CHECKSUM_APP_PRIVATE_KEY }}

- name: Push the update, if any
env:
BRANCH: ${{ github.ref_name }}
TOKEN: ${{ steps.app-token.outputs.token || secrets.UPDATE_AQUA_CHECKSUM_TOKEN || github.token }}
run: |
[ -z "$(git status --porcelain)" ] && { echo "checksums and baseline already in sync"; exit 0; }
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# `--all` is deliberate, not sloppiness. The tree is fully accounted
# for: a fresh checkout, aqua rooted outside the workspace, and
# exactly two writers between checkout and here — update-checksum
# and the pinned limen's fix. Whatever is dirty IS the payload. An
# enumerated path list would be wrong: the converge step's job is to
# commit whatever the NEW limen's baseline says, and a list baked
# into the older running workflow cannot know that surface (the
# updated workflow arrives in the very commit being built). Nor
# would a list add safety — the only writer that could plant a file
# is limen fix itself, and .limen/.github would be on any list.
git add --all
# Signed-off-by: `just do lint commits` enforces DCO on the PR range,
# bot commits included.
git commit -m "chore: update aqua checksums and converge the limen baseline" \
-m "Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>"
git push "https://x-access-token:${TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "HEAD:${BRANCH}"
26 changes: 25 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
.DS_Store
.idea
_*

# --- added by limen fix: baseline patterns ---
Thumbs.db
Desktop.ini
.vscode/
*.swp
*~
*.local
_scratch/
WIP_*
/build
/target/
**/*.rs.bk
*.pdb
node_modules/
.svelte-kit/
.vite/
vite.config.*.timestamp-*
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# --- added by limen fix: baseline patterns ---
/tmp
Loading
Loading