Releases: moduloTech/autodev
v1.0.0-alpha.17
Added
- New in-app help pages:
/helprendersdocs/usage/autodev-functional-usage.md(open to all signed-in users),/admin/helprendersdocs/usage/autodev-technical-usage.md(admin-gated viaAdminApplicationController). Both flow throughHelpDoc.render(:functional | :technical): strips the pandoc YAML frontmatter +\newpagemarkers carried over from the PDF flow, rewrites screenshot refs fromscreenshots/X.pngto/help/images/X.png, then renders through Redcarpet (GFM tables, fenced code, autolinking, strikethrough). Shared image endpoint at/help/images/:filename(allowlisted filename pattern,image/png, 1h browser cache). Single genericWeb::Views::HelpPhlex shell parameterized byactive+title_key+subtitle_key— one view, two pages. Sidebar gains an "Aide" entry (everyone) and an admin-only "Aide technique" entry; new locale keysweb_nav_help/web_nav_admin_help/web_help_title|subtitle/web_admin_help_title|subtitlein FR + EN. New.help-docCSS scope restores readable typography (h1/h2/h3 hierarchy, tables, image framing, blockquote, inline code chips).redcarpet ~> 3.6added to the Gemfile. The manualmd2pdfworkflow is retired — the markdown sources are the single artefact, previewed live in the browser.
v1.0.0-alpha.16
Added
- Paired French usage guides under
docs/usage/:autodev-functional-usage.md(non-admin, jargon-free) andautodev-technical-usage.md(admin routes, config, AASM, polling, pipeline matrix, errors, on-disk state) with 11 screenshots from the prod dashboard. Both compile to PDF viapandoc --pdf-engine=xelatex.
Changed
- Sidebar user strip now shows the signed-in user (avatar = first letter of email's local part, name = local part, "Sign out" link replacing the role text). The floating
render_session_widgetinLayoutis gone;Components::Sidebar.newtakescurrent_user_email:+csrf_token:. The sign-out button used to dead-end onNo route matches [DELETE] "/users/sign_out"because:database_authenticatableis off — a newUsers::SessionsController#destroyfills the gap, mounted atdelete '/users/sign_out', withskip_forgery_protectionto match Devise's stock posture. - Dashboard KPI tiles are now clickable:
Components::Kpigot an optionalhref:, the 5 cards route to/issues?tab=<...>. /admin/usersredesigned against the app's design system (app-shell+Sidebar+Topbar+Card, CSS-grid table, mobile cards). All strings localized. Sidebar gains "Users" + "Jobs" admin-only entries.AssetsControllerrewritten as a single catch-all/assets/*pathresolving through Propshaft's load_path. Mission Control assets now served via the same path. Cache headers split: digested URLs getimmutable, stable URLs getno-cache.- Recurring jobs (
AutodevPollJob,SyncGitlabMembershipsJob) no longer auto-fire in development (emptydevelopment:block inconfig/recurring.yml).bin/autodevprints a banner with the manual trigger commands. Rakefilenow loads Rails tasks —bin/rails autodev:*tasks work directly.- Dev
/sign_inpage surfaces missing Azure SSO credentials and emptyprojectstable with actionable banners. docs/autospec.mdgains section L documenting the SSE thread-parking limitation observed on AutoDev, the short-term mitigation in place, and the criteria for reconsidering ActionCable + Solid Cable at the AutoSpec implementation.
Removed
/list/:statusroute,ListController,Web::Views::List, and the fiveweb_list_*i18n keys. Redundant with/issues?tab=…and never linked.
Fixed
- Dashboard freezing on repeated F5 / multiple open tabs (Puma thread saturation by parked
/streamconnections). Two mitigations: client-sidepagehidelistener closes the EventSource so the FIN reaches the server immediately;HEARTBEAT_INTERVALlowered from 15 → 5s for silent client deaths. Architectural fix (ActionCable + Solid Cable) deferred to AutoSpec — seedocs/autospec.md§L. Web.configreverted tonilafter any code reload in development, breaking the OAuth callback withConfigError (Missing GitLab API token). Loader switched fromafter_initialize(boot-only) toto_prepare(re-runs on every reload)./assets/css/*.css404 in development.Propshaft::Servermiddleware dropped; all/assets/*go throughAssetsController#show.
v1.0.0-alpha.9
Hotfix #2 for the alpha.7 cutover. Required after alpha.8 unblocked the redirect loop but /users/auth/entra_id still 404'd.
Fixed
- OmniAuth path_prefix mismatch. OmniAuth strategies default to
path_prefix = '/auth', but Devise'sdevise_for :usersmounts sign-in routes at/users/auth/:provider. Without explicitpath_prefix, the middleware never matched the URL and Devise's hardcodedUsers::OmniauthCallbacksController#passthruaction returned 404 ("Not found. Authentication passthru.") as the documented fallback. Fix: passpath_prefix: '/users/auth'in theconfig.omniauth :entra_idcall.
Added
azure:block in~/.autodev/config.yml(client_id,client_secret,tenant_id). Read priority: ENV (AZURE_AD_CLIENT_ID/AZURE_AD_CLIENT_SECRET/AZURE_AD_TENANT_ID) → config.yml → stub. The config-file path is the canonical place for production since launchd-managed services don't inherit the operator's shell env. Template comment added inlib/autodev/config.rb.- The supervisor forwards
AZURE_AD_*env vars to the children when set in the parent's env.
Setup on bobette (if not done yet)
# 1. Register the app in Azure portal (one-off). Get client_id /
# client_secret / tenant_id from the registration page.
# Add the redirect URI: http://127.0.0.1:4567/users/auth/entra_id/callback
# (or the NetBird URL the dashboard is exposed at).
# 2. Add credentials to ~/.autodev/config.yml:
cat >> ~/.autodev/config.yml <<EOF2
azure:
client_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
tenant_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
EOF2
# 3. Upgrade autodev + restart.
brew update && brew upgrade autodev
brew services restart autodev
# 4. Open the dashboard → redirects to Microsoft → sign in → callback → dashboard.If azure: isn't configured, the dashboard will still attempt the omniauth flow but Microsoft will reject the stub client_id (visible error, not a silent 404).
v1.0.0-alpha.8
Hotfix for the alpha.7 cutover. Anyone who upgraded to alpha.7 needs this — alpha.7's gating caused the dashboard to loop on every URL (ERR_TOO_MANY_REDIRECTS in the browser, Completed 401 Unauthorized flood in production.log).
Fixed
- Devise redirect loop after alpha.7 gating. Two-part fix in
config/initializers/devise.rb:Rails.application.config.to_prepare { DeviseController.skip_before_action :authenticate_user!, raise: false }so Devise's own controllers stop inheriting the globalauthenticate_user!PR3 added —/users/sign_in,/users/sign_out, and the failure-app redirect target are reachable to anonymous traffic again.- New
EntraIdFailureApp < Devise::FailureAppredirects unauthenticated callers straight to/users/auth/entra_id(the omniauth handshake) instead of the default/users/sign_in. The Devise default would have rendered the gem'ssessions/newview that expects:database_authenticatablefields we don't ship; the omniauth handshake is the only sign-in path this app has.
Recovery on bobette
brew update && brew upgrade autodev
brew services restart autodevNo DB or config changes. Browser should now redirect / → /users/auth/entra_id → Microsoft → callback → dashboard.
Why PR3's gating tests didn't catch this
gating_test.rb only asserted assert_response :redirect on /, i.e. it stopped at the first 302 without following the chain. The bug only manifests when the redirect target ALSO requires auth. Worth adding a follow_redirect! assertion next pass.
v1.0.0-alpha.15
Hotfix #7 for the alpha.7 cutover. The OAuth flow itself works end-to-end now (alpha.14 confirmed); this closes the seeded-admin → first-SSO race that left the very first callback unable to persist the user row.
Fixed
User.from_omniauthraisedActiveRecord::RecordInvalid (Validation failed: Email has already been taken)on the very first SSO sign-in whenautodev:seed_adminhad previously seeded the operator's User row. The seeded row carriesemail+admin: truebut nomicrosoft_uid;find_or_initialize_by(microsoft_uid:)missed it and tried to INSERT a new row that collided on the unique-email index. Fix: fall back to a case-insensitive email lookup when the uid lookup misses, and attach the Entra uid to the existing row.
Recovery on bobette
brew update && brew upgrade autodev
brew services restart autodevThe full flow should finally land: dashboard → 302 → /sign_in → "Se connecter avec Microsoft" → Microsoft sign-in → callback → admin row gets its microsoft_uid attached → dashboard visible as ciappa_m@modulotech.fr.
v1.0.0-alpha.14
Hotfix #6 for the alpha.7 cutover. alpha.13's assume_ssl fix made the origin check pass, but the CSRF token comparison kept failing through omniauth-rails_csrf_protection 1.0.2. Defence delegated to OAuth state.
Fixed
- POST
/users/auth/entra_idstill 422'd after alpha.13. Theunverified_request_warning_messagetext (Can't verify CSRF token authenticity.and not the origin-mismatch variant) confirmed the origin check passed — theassume_sslfix from alpha.13 was correct. The remaining failure was at the masked-token-vs-session comparison;omniauth-rails_csrf_protection1.0.2 has an interop bug with Rails 8's CSRF storage strategy that isn't worth chasing right now. SetOmniAuth.config.request_validation_phase = nilso the gem stops adjudicating the request phase. The OAuthstateparameter (generated at request phase, validated on callback) is the canonical CSRF defence for the OAuth handshake; combined withallowed_request_methods = [:post], the registered redirect URI at Entra, and the NetBird mesh trust model, the defence-in-depth posture stays reasonable.
Recovery on bobette
brew update && brew upgrade autodev
brew services restart autodevThe full flow should finally complete: dashboard → 302 → /sign_in → click "Se connecter avec Microsoft" → POST /users/auth/entra_id → OmniAuth strategy intercepts and redirects to login.microsoftonline.com → sign in → callback → dashboard.
v1.0.0-alpha.13
Hotfix #5 for the alpha.7 cutover. The alpha.12 /sign_in POST flow was sound but the CSRF check 422'd because Rails couldn't tell that the request came in over HTTPS (NetBird terminates TLS, Puma sees plain HTTP).
Fixed
- 422 InvalidAuthenticityToken on POST
/users/auth/entra_idwhen accessed through the NetBird reverse proxy.verified_request?comparesrequest.origin(browser-set,https://autodev.netbird.<tenant>) againstrequest.base_url(constructed from the scheme Rails sees,http://127.0.0.1:4567). Mismatch → false → 422 even when the token IS correct. Fix:config.assume_ssl = trueinconfig/environments/production.rbso Rails treats every request as HTTPS (i.e. trusts the proxy's TLS termination).
Recovery on bobette
brew update && brew upgrade autodev
brew services restart autodevVisit the dashboard → 302 → /sign_in → "Se connecter avec Microsoft" → Microsoft login → callback → dashboard.
Quick sanity check after restart — request.scheme should be https from Puma's POV:
curl -s -X POST -H "X-Forwarded-Proto: https" -H "Origin: https://autodev.netbird.modulotech.fr" \
https://autodev.netbird.modulotech.fr/users/auth/entra_id 2>&1 | head -20Should redirect to login.microsoftonline.com instead of 422'ing.
v1.0.0-alpha.12
Switches the omniauth request phase from GET-allowed (alpha.11) to POST-only via an interim /sign_in page. Defence-in-depth restored to the canonical Devise + omniauth posture (CSRF token on the form, OAuth state parameter on the callback).
Changed
-
Anonymous traffic now lands on a small
/sign_inpage rendered bySignInController. The page has a single button — clicking it POSTs to/users/auth/entra_idwith a CSRF token, OmniAuth strategy intercepts, redirect to Microsoft, callback, sign-in done. -
OmniAuth.config.allowed_request_methodsback to the default[:post]. The alpha.11 relaxation is gone. -
EntraIdFailureApp.redirect_urlnow returns/sign_in(was/users/auth/entra_id). -
New locale keys:
web_sign_in_title,web_sign_in_subtitle,web_sign_in_button(FR + EN).
UX
One extra click vs alpha.11 (anonymous → button → Microsoft instead of anonymous → Microsoft directly). The button screen also acts as a clear sign-in CTA for first-time visitors.
Recovery on bobette
brew update && brew upgrade autodev
brew services restart autodevVisit the dashboard → 302 → /sign_in → click "Se connecter avec Microsoft" → Microsoft login → callback → dashboard.
v1.0.0-alpha.11
Hotfix #4 for the alpha.7 cutover. The three previous hotfixes brought the omniauth middleware to a working state (path_prefix matched, real credentials loaded), but the strategy still didn't intercept the GET.
Fixed
- OmniAuth 2.x rejects GET on the request phase by default.
OmniAuth.config.allowed_request_methods = [:post]is the gem's CSRF hardening default — a GET request to/users/auth/entra_idpasses through the strategy middleware without being intercepted and falls through toUsers::OmniauthCallbacksController#passthru(404). Our flow is GET-driven:EntraIdFailureAppreturns a 302 to the omniauth path and the browser follows with GET. Fix: setOmniAuth.config.allowed_request_methods = %i[get post]inconfig/initializers/devise.rb.
The defence-in-depth this disables is the gem-level CSRF check on the request phase; the OAuth state parameter (set by OmniAuth, validated on callback) is the canonical defence against forged sign-in initiations, and autodev runs behind a NetBird mesh so the attack surface is already narrow.
Recovery on bobette
brew update && brew upgrade autodev
brew services restart autodevVisit https://autodev.netbird.modulotech.fr → should redirect to login.microsoftonline.com → sign in → redirect back to the dashboard's callback → sign-in complete.
If it still hits passthru: confirm via bin/rails runner 'puts OmniAuth.config.allowed_request_methods.inspect' — should print [:get, :post].
v1.0.0-alpha.10
Hotfix #3 for the alpha.7 cutover. The path_prefix fix from alpha.9 is confirmed working; alpha.10 closes the second half of the same issue — the credentials weren't being read from ~/.autodev/config.yml.
Fixed
azure:config-file block silently ignored. Devise registered the omniauth provider with stub credentials even when~/.autodev/config.ymlhad valid Microsoft 365 credentials. Root cause:config/initializers/load_autodev_config.rbpopulatesWeb.configinsideRails.application.config.after_initialize, which fires AFTER every initializer has run. By the timedevise.rbreadsWeb.config['azure'], the value is still nil. Devise's omniauth_configs hash is locked in at the engine's middleware-build step, so a post-hoc fixup isn't reachable. Fix: read~/.autodev/config.ymldirectly indevise.rb(synchronous YAML load, no dependency on initializer order).
Recovery on bobette
brew update && brew upgrade autodev
brew services restart autodev~/.autodev/config.yml should already have the azure: block from the alpha.9 setup; nothing more to change on disk. Visit the dashboard → redirects to Microsoft → sign in → callback → dashboard.
Sanity check after upgrade
cd "$(brew --prefix autodev)/libexec"
PATH=\"$(brew --prefix ruby)/bin:\$PATH\" RAILS_ENV=production bin/rails runner '
mw = Rails.application.middleware.detect { |m| m.klass.name.include?(\"EntraId\") }
args = mw.args.first
puts \"client_id: #{args[:client_id]}\" # should be your real Azure client ID, not stub-client-id
puts \"path_prefix: #{args[:path_prefix]}\" # should be /users/auth
'