Skip to content
Merged
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
35 changes: 20 additions & 15 deletions .github/scripts/verify-keycloak-compat.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ lock="themes/apiary/keycloak.lock"
git_tag="$(sed -n 's/^git_tag = //p' "$lock")"
[[ -n "$git_tag" ]] || { echo "no git_tag found in $lock" >&2; exit 1; }

base_url="https://raw.githubusercontent.com/keycloak/keycloak/${git_tag}/themes/src/main/resources/theme/keycloak.v2/login"

fail=0
# $1: base_url $2: rel_path $3: section (for the hash lookup below)
check_one() {
local rel_path="$1" expected
expected="$(sed -n "s#^${rel_path//./\\.} = ##p" "$lock")"
local base_url="$1" rel_path="$2" section="$3" expected
expected="$(awk -v s="[$section]" 'BEGIN{RS="";FS="\n"} $0 ~ s' "$lock" | sed -n "s#^${rel_path//./\\.} = ##p")"
if [[ -z "$expected" ]]; then
echo "FAIL: no recorded hash for '$rel_path' in $lock" >&2
echo "FAIL: no recorded hash for '$rel_path' in $lock's [$section]" >&2
fail=1
return
fi
Expand All @@ -40,21 +39,27 @@ check_one() {
echo "FAIL: ${rel_path} drifted from the pinned Keycloak ${git_tag} release" >&2
echo " expected sha256: ${expected}" >&2
echo " actual sha256: ${actual}" >&2
echo " This theme's CSS/DOM assumptions (theme.properties parent/styles," >&2
echo " template.ftl structure, or keycloak.v2's own stylesheet) have not" >&2
echo " been re-validated against this change. If this is an intentional" >&2
echo " Keycloak upgrade, update themes/apiary/keycloak.lock's git_tag/" >&2
echo " digest/hashes together with a full theme test pass -- see" >&2
echo " docs/THEME-GUIDE.md. If it isn't, something is wrong upstream or" >&2
echo " with this pin; do not just update the hash to make CI pass." >&2
echo " This theme's CSS/DOM/FTL assumptions have not been re-validated" >&2
echo " against this change. If this is an intentional Keycloak upgrade," >&2
echo " update themes/apiary/keycloak.lock's git_tag/digest/hashes together" >&2
echo " with a full theme test pass -- see docs/THEME-GUIDE.md. If it" >&2
echo " isn't, something is wrong upstream or with this pin; do not just" >&2
echo " update the hash to make CI pass." >&2
fail=1
else
echo "OK: ${rel_path} matches Keycloak ${git_tag}"
fi
}

check_one "theme.properties"
check_one "template.ftl"
check_one "resources/css/styles.css"
login_base="https://raw.githubusercontent.com/keycloak/keycloak/${git_tag}/themes/src/main/resources/theme/keycloak.v2/login"
check_one "$login_base" "theme.properties" "upstream_files"
check_one "$login_base" "template.ftl" "upstream_files"
check_one "$login_base" "resources/css/styles.css" "upstream_files"

# #91: themes/apiary/email/html/template.ftl overrides base/email's own
# file, not keycloak.v2/login's -- a separate upstream tree, no
# theme.properties there to pin (base/email doesn't have one at all).
email_base="https://raw.githubusercontent.com/keycloak/keycloak/${git_tag}/themes/src/main/resources/theme/base/email"
check_one "$email_base" "html/template.ftl" "email_upstream_files"

exit "$fail"
24 changes: 17 additions & 7 deletions docs/THEME-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,35 @@ account recovery is an administrator-driven credential reset.
`themes/apiary/keycloak.lock` is the compatibility record: the exact
image/digest APIARY deploys, sha256 hashes of the upstream `keycloak.v2`
files this theme's CSS actually depends on (`theme.properties`,
`template.ftl`, `resources/css/styles.css`), and the specific DOM IDs/classes
`login.css` reaches into. `.github/workflows/theme.yml` runs
`template.ftl`, `resources/css/styles.css`), the specific DOM IDs/classes
`login.css` reaches into, and (`[email_upstream_files]`) the one upstream
`base/email` file `themes/apiary/email/html/template.ftl` replaces — a
different upstream tree from `keycloak.v2/login`, since `base/email` has no
`keycloak.v2` override and no `theme.properties` of its own for this
release. `.github/workflows/theme.yml` runs
`.github/scripts/verify-keycloak-compat.sh` on every push/PR, which
re-fetches those files fresh from the pinned tag and fails CI with a
readable diff if they've drifted from what's recorded — this is a CSS-only
child theme, so a change to keycloak.v2's markup or class names is a real
compatibility break even though no line in this repo changed.
compatibility break even though no line in this repo changed. The account
console (`keycloak.v3`) has no equivalent file-hash check: it's a compiled
React SPA with no individually-fetchable FreeMarker/CSS files to hash, so
its upgrade-compatibility coverage is a DOM-hook selector scan instead —
see `test/specs/account.spec.ts`'s "Account theme DOM-hook compatibility"
describe block.

A Keycloak version bump requires, in order:

1. Update `[keycloak]` in `keycloak.lock` to the new image, digest, and
matching `git_tag`/`git_commit`.
2. Re-derive `[upstream_files]`'s hashes against the new tag (the same
`raw.githubusercontent.com/keycloak/keycloak/<tag>/...` paths
`verify-keycloak-compat.sh` fetches) and update them.
2. Re-derive `[upstream_files]`'s and `[email_upstream_files]`'s hashes
against the new tag (the same `raw.githubusercontent.com/keycloak/keycloak/<tag>/...`
paths `verify-keycloak-compat.sh` fetches) and update them.
3. Re-derive `[required_dom_hooks]` if `login.css` gained/lost selectors
(regenerate command is in the file's own comment).
4. Run the full pre-merge checklist above against the new image, plus every
flow #103's interaction layer touches once that exists.
flow #103's interaction layer touches once that exists, plus
`test/specs/account.spec.ts`'s DOM-hook scan and `test/specs/email.spec.ts`.
5. Only then does `verify-keycloak-compat.sh` pass again — do not edit the
recorded hash to make a real drift finding go away without doing 1-4.

Expand Down
18 changes: 12 additions & 6 deletions test/fixtures/realm-export.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@
"credentials": [
{"type": "password", "value": "test-password-only", "temporary": false}
],
"requiredActions": ["CONFIGURE_TOTP"]
"requiredActions": ["CONFIGURE_TOTP"],
"realmRoles": ["default-roles-test-apiary"]
},
{
"username": "test-user-verify-email",
Expand All @@ -140,7 +141,8 @@
"credentials": [
{"type": "password", "value": "test-password-only", "temporary": false}
],
"requiredActions": ["VERIFY_EMAIL"]
"requiredActions": ["VERIFY_EMAIL"],
"realmRoles": ["default-roles-test-apiary"]
},
{
"username": "test-user-webauthn-register",
Expand All @@ -150,7 +152,8 @@
"credentials": [
{"type": "password", "value": "test-password-only", "temporary": false}
],
"requiredActions": ["webauthn-register"]
"requiredActions": ["webauthn-register"],
"realmRoles": ["default-roles-test-apiary"]
},
{
"username": "test-user-consent",
Expand All @@ -160,7 +163,8 @@
"credentials": [
{"type": "password", "value": "test-password-only", "temporary": false}
],
"requiredActions": []
"requiredActions": [],
"realmRoles": ["default-roles-test-apiary"]
},
{
"username": "test-user-multi-factor",
Expand All @@ -170,7 +174,8 @@
"credentials": [
{"type": "password", "value": "test-password-only", "temporary": false}
],
"requiredActions": ["CONFIGURE_TOTP", "webauthn-register"]
"requiredActions": ["CONFIGURE_TOTP", "webauthn-register"],
"realmRoles": ["default-roles-test-apiary"]
},
{
"username": "test-user-multi-factor-2",
Expand All @@ -180,7 +185,8 @@
"credentials": [
{"type": "password", "value": "test-password-only", "temporary": false}
],
"requiredActions": ["CONFIGURE_TOTP", "webauthn-register"]
"requiredActions": ["CONFIGURE_TOTP", "webauthn-register"],
"realmRoles": ["default-roles-test-apiary"]
}
]
}
Loading
Loading