mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
patch.py went one character over when OMNIAUTH_INJECTION became the omniauth_injection() call inb192415a5, so the formatter pulls the trailing concatenation onto its own line. test_workflow_dispatch_name_length.py was already unformatted before this session; it arrived withe7fadf02fand the autoformat run only surfaced it. Carried along so the formatter stops reopening the diff on every run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
175 lines
7.0 KiB
Python
175 lines
7.0 KiB
Python
# nocheck: mirrored-unit-test - rewrites files inside the installed decidim-core gem,
|
|
# located by globbing /usr/local/bundle; the patch targets only exist in the running
|
|
# container
|
|
"""Patches Decidim gem and app files for OpenID Connect and S3 storage.
|
|
|
|
Decidim 0.31 (Rails 7.2) removed `config/secrets.yml`; provider configuration
|
|
is sourced from runtime ENV vars instead. The patches below:
|
|
|
|
- Locate decidim-core gem dynamically (`decidim-core-*`) so any 0.3x version works.
|
|
- Register `:openid_connect` in `Decidim.omniauth_providers` at the top of
|
|
decidim-core's omniauth.rb initializer. Decidim::User's
|
|
`devise :omniauthable, omniauth_providers: Decidim::OmniauthProvider.available.keys`
|
|
reads that registry, so registration must happen before the User model loads
|
|
for Rails to generate the `user_openid_connect_omniauth_authorize_path`
|
|
URL helper the homepage's `_omniauth_buttons.html.erb` partial calls.
|
|
- Inject an `openid_connect` provider registration into decidim-core's
|
|
omniauth.rb initializer, gated on `ENV["OIDC_ENABLED"] == "true"`.
|
|
- Short-circuit the oauth icon helper so the login-box-line icon is returned
|
|
for `:openid_connect` without hitting the (organization-bound) registry.
|
|
- Fix the omniauth registration form action URL: Decidim 0.31.3 passes
|
|
``resource_name`` (``:user``) as a positional arg to
|
|
``omniauth_registrations_path``; Rails then treats it as a ``:format`` on
|
|
the formatless route, yielding ``/omniauth_registrations.user`` which
|
|
returns 406 and silently prevents user creation.
|
|
- Add ``force_path_style: true`` to the ``s3`` service in
|
|
``config/storage.yml``. The upstream storage.yml omits it, so the AWS SDK
|
|
defaults to virtual-host addressing; SeaweedFS only serves path-style, so
|
|
ActiveStorage must force it. ActiveStorage::Service::S3Service forwards any
|
|
extra storage.yml key straight to ``Aws::S3::Resource.new`` (and thus the
|
|
S3 client), so the flag takes effect without further wiring.
|
|
"""
|
|
|
|
import glob
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
|
|
OMNIAUTH_REGISTRATION = r"""if ENV["OIDC_ENABLED"].to_s == "true"
|
|
Decidim.omniauth_providers[:openid_connect] = {
|
|
enabled: true,
|
|
icon: "login-box-line"
|
|
}
|
|
end
|
|
|
|
"""
|
|
|
|
|
|
def omniauth_injection() -> str:
|
|
"""Read the provider builder fragment from the ruby directory the build mounts.
|
|
|
|
Returns:
|
|
The fragment's content, prefixed with a newline.
|
|
"""
|
|
fragment = Path(os.environ["DECIDIM_PATCH_RUBY_DOCKER"]) / "omniauth_provider.rb"
|
|
return "\n" + fragment.read_text()
|
|
|
|
|
|
def patch_omniauth_rb(content: str) -> str:
|
|
"""Register openid_connect in Decidim.omniauth_providers and add provider builder call."""
|
|
if 'ENV["OIDC_ENABLED"]' in content:
|
|
return content
|
|
content = (
|
|
re.sub(r"( end\nend\s*)$", omniauth_injection() + r"\1", content.rstrip())
|
|
+ "\n"
|
|
)
|
|
return OMNIAUTH_REGISTRATION + content
|
|
|
|
|
|
def patch_omniauth_helper_rb(content: str) -> str:
|
|
"""Return login-box-line icon for openid_connect to avoid registry lookup failure."""
|
|
if "provider.to_sym == :openid_connect" in content:
|
|
return content
|
|
return content.replace(
|
|
" def oauth_icon(provider)",
|
|
' def oauth_icon(provider)\n return icon("login-box-line") if provider.to_sym == :openid_connect',
|
|
)
|
|
|
|
|
|
def patch_omniauth_registration_new_erb(content: str) -> str:
|
|
"""Drop the positional ``resource_name`` arg from the form URL helper.
|
|
|
|
Decidim 0.31.3 renders ``decidim.omniauth_registrations_path(resource_name)``.
|
|
The named route has no dynamic segments so Rails binds the symbol to
|
|
``:format``, producing ``/omniauth_registrations.user`` which 406s before
|
|
the controller runs.
|
|
"""
|
|
return content.replace(
|
|
"decidim.omniauth_registrations_path(resource_name)",
|
|
"decidim.omniauth_registrations_path",
|
|
)
|
|
|
|
|
|
def patch_active_storage_env_rb(content: str) -> str:
|
|
"""Read STORAGE_PROVIDER for ActiveStorage like production.rb does.
|
|
|
|
``config/environments/development.rb`` hardcodes
|
|
``config.active_storage.service = :local``, so a deployment running with
|
|
``RAILS_ENV=development`` writes uploads to the local Disk service and never
|
|
reaches the configured S3 (SeaweedFS) bucket. Mirror production.rb so the
|
|
service follows ``STORAGE_PROVIDER`` in every environment.
|
|
"""
|
|
target = "config.active_storage.service = :local"
|
|
replacement = 'config.active_storage.service = Decidim::Env.new("STORAGE_PROVIDER", "local").to_s'
|
|
if replacement in content or target not in content:
|
|
return content
|
|
return content.replace(target, replacement)
|
|
|
|
|
|
def patch_storage_yml(content: str) -> str:
|
|
"""Force path-style addressing for the s3 service so SeaweedFS is reachable."""
|
|
if "force_path_style" in content:
|
|
return content
|
|
return re.sub(
|
|
r"(^s3:\n[\s\S]*?^[ \t]+service: S3\n)",
|
|
r"\1 force_path_style: true\n",
|
|
content,
|
|
count=1,
|
|
flags=re.MULTILINE,
|
|
)
|
|
|
|
|
|
def find_app_file(relative_path: str) -> str:
|
|
"""Resolve a file inside the generated Decidim app at /code."""
|
|
return f"/code/{relative_path}"
|
|
|
|
|
|
def find_decidim_core_file(relative_path: str) -> str:
|
|
"""Resolve a file inside the installed decidim-core gem, version-agnostic."""
|
|
pattern = f"/usr/local/bundle/gems/decidim-core-*/{relative_path}"
|
|
matches = sorted(glob.glob(pattern))
|
|
if not matches:
|
|
raise FileNotFoundError(f"No decidim-core gem found matching {pattern}")
|
|
return matches[-1]
|
|
|
|
|
|
if __name__ == "__main__":
|
|
omniauth_path = find_decidim_core_file("config/initializers/omniauth.rb")
|
|
with Path(omniauth_path).open() as f:
|
|
content = f.read()
|
|
with Path(omniauth_path).open("w") as f:
|
|
f.write(patch_omniauth_rb(content))
|
|
print(f"{omniauth_path} patched")
|
|
|
|
helper_path = find_decidim_core_file("app/helpers/decidim/omniauth_helper.rb")
|
|
with Path(helper_path).open() as f:
|
|
content = f.read()
|
|
with Path(helper_path).open("w") as f:
|
|
f.write(patch_omniauth_helper_rb(content))
|
|
print(f"{helper_path} patched")
|
|
|
|
for view_relative in (
|
|
"app/views/decidim/devise/omniauth_registrations/new.html.erb",
|
|
"app/views/decidim/devise/omniauth_registrations/new_tos_fields.html.erb",
|
|
):
|
|
registration_view = find_decidim_core_file(view_relative)
|
|
with Path(registration_view).open() as f:
|
|
content = f.read()
|
|
with Path(registration_view).open("w") as f:
|
|
f.write(patch_omniauth_registration_new_erb(content))
|
|
print(f"{registration_view} patched")
|
|
|
|
dev_env_path = find_app_file("config/environments/development.rb")
|
|
with Path(dev_env_path).open() as f:
|
|
content = f.read()
|
|
with Path(dev_env_path).open("w") as f:
|
|
f.write(patch_active_storage_env_rb(content))
|
|
print(f"{dev_env_path} patched")
|
|
|
|
storage_path = find_app_file("config/storage.yml")
|
|
with Path(storage_path).open() as f:
|
|
content = f.read()
|
|
with Path(storage_path).open("w") as f:
|
|
f.write(patch_storage_yml(content))
|
|
print(f"{storage_path} patched")
|