Skip to content

Releases: moduloTech/autodev

v1.0.0-alpha.17

12 Jun 10:13

Choose a tag to compare

Added

  • New in-app help pages: /help renders docs/usage/autodev-functional-usage.md (open to all signed-in users), /admin/help renders docs/usage/autodev-technical-usage.md (admin-gated via AdminApplicationController). Both flow through HelpDoc.render(:functional | :technical): strips the pandoc YAML frontmatter + \newpage markers carried over from the PDF flow, rewrites screenshot refs from screenshots/X.png to /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 generic Web::Views::Help Phlex shell parameterized by active + title_key + subtitle_key — one view, two pages. Sidebar gains an "Aide" entry (everyone) and an admin-only "Aide technique" entry; new locale keys web_nav_help / web_nav_admin_help / web_help_title|subtitle / web_admin_help_title|subtitle in FR + EN. New .help-doc CSS scope restores readable typography (h1/h2/h3 hierarchy, tables, image framing, blockquote, inline code chips). redcarpet ~> 3.6 added to the Gemfile. The manual md2pdf workflow is retired — the markdown sources are the single artefact, previewed live in the browser.

v1.0.0-alpha.16

12 Jun 07:20

Choose a tag to compare

Added

  • Paired French usage guides under docs/usage/: autodev-functional-usage.md (non-admin, jargon-free) and autodev-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 via pandoc --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_widget in Layout is gone; Components::Sidebar.new takes current_user_email: + csrf_token:. The sign-out button used to dead-end on No route matches [DELETE] "/users/sign_out" because :database_authenticatable is off — a new Users::SessionsController#destroy fills the gap, mounted at delete '/users/sign_out', with skip_forgery_protection to match Devise's stock posture.
  • Dashboard KPI tiles are now clickable: Components::Kpi got an optional href:, the 5 cards route to /issues?tab=<...>.
  • /admin/users redesigned 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.
  • AssetsController rewritten as a single catch-all /assets/*path resolving through Propshaft's load_path. Mission Control assets now served via the same path. Cache headers split: digested URLs get immutable, stable URLs get no-cache.
  • Recurring jobs (AutodevPollJob, SyncGitlabMembershipsJob) no longer auto-fire in development (empty development: block in config/recurring.yml). bin/autodev prints a banner with the manual trigger commands.
  • Rakefile now loads Rails tasks — bin/rails autodev:* tasks work directly.
  • Dev /sign_in page surfaces missing Azure SSO credentials and empty projects table with actionable banners.
  • docs/autospec.md gains 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/:status route, ListController, Web::Views::List, and the five web_list_* i18n keys. Redundant with /issues?tab=… and never linked.

Fixed

  • Dashboard freezing on repeated F5 / multiple open tabs (Puma thread saturation by parked /stream connections). Two mitigations: client-side pagehide listener closes the EventSource so the FIN reaches the server immediately; HEARTBEAT_INTERVAL lowered from 15 → 5s for silent client deaths. Architectural fix (ActionCable + Solid Cable) deferred to AutoSpec — see docs/autospec.md §L.
  • Web.config reverted to nil after any code reload in development, breaking the OAuth callback with ConfigError (Missing GitLab API token). Loader switched from after_initialize (boot-only) to to_prepare (re-runs on every reload).
  • /assets/css/*.css 404 in development. Propshaft::Server middleware dropped; all /assets/* go through AssetsController#show.

v1.0.0-alpha.9

10 Jun 08:29

Choose a tag to compare

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's devise_for :users mounts sign-in routes at /users/auth/:provider. Without explicit path_prefix, the middleware never matched the URL and Devise's hardcoded Users::OmniauthCallbacksController#passthru action returned 404 ("Not found. Authentication passthru.") as the documented fallback. Fix: pass path_prefix: '/users/auth' in the config.omniauth :entra_id call.

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 in lib/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

10 Jun 08:11

Choose a tag to compare

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:
    1. Rails.application.config.to_prepare { DeviseController.skip_before_action :authenticate_user!, raise: false } so Devise's own controllers stop inheriting the global authenticate_user! PR3 added — /users/sign_in, /users/sign_out, and the failure-app redirect target are reachable to anonymous traffic again.
    2. New EntraIdFailureApp < Devise::FailureApp redirects 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's sessions/new view that expects :database_authenticatable fields 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 autodev

No 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

10 Jun 22:23

Choose a tag to compare

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_omniauth raised ActiveRecord::RecordInvalid (Validation failed: Email has already been taken) on the very first SSO sign-in when autodev:seed_admin had previously seeded the operator's User row. The seeded row carries email + admin: true but no microsoft_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 autodev

The 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

10 Jun 22:16

Choose a tag to compare

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_id still 422'd after alpha.13. The unverified_request_warning_message text (Can't verify CSRF token authenticity. and not the origin-mismatch variant) confirmed the origin check passed — the assume_ssl fix from alpha.13 was correct. The remaining failure was at the masked-token-vs-session comparison; omniauth-rails_csrf_protection 1.0.2 has an interop bug with Rails 8's CSRF storage strategy that isn't worth chasing right now. Set OmniAuth.config.request_validation_phase = nil so the gem stops adjudicating the request phase. The OAuth state parameter (generated at request phase, validated on callback) is the canonical CSRF defence for the OAuth handshake; combined with allowed_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 autodev

The 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

10 Jun 21:47

Choose a tag to compare

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_id when accessed through the NetBird reverse proxy. verified_request? compares request.origin (browser-set, https://autodev.netbird.<tenant>) against request.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 = true in config/environments/production.rb so 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 autodev

Visit 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 -20

Should redirect to login.microsoftonline.com instead of 422'ing.

v1.0.0-alpha.12

10 Jun 21:28

Choose a tag to compare

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_in page rendered by SignInController. The page has a single button — clicking it POSTs to /users/auth/entra_id with a CSRF token, OmniAuth strategy intercepts, redirect to Microsoft, callback, sign-in done.

  • OmniAuth.config.allowed_request_methods back to the default [:post]. The alpha.11 relaxation is gone.

  • EntraIdFailureApp.redirect_url now 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 autodev

Visit the dashboard → 302 → /sign_in → click "Se connecter avec Microsoft" → Microsoft login → callback → dashboard.

v1.0.0-alpha.11

10 Jun 21:22

Choose a tag to compare

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_id passes through the strategy middleware without being intercepted and falls through to Users::OmniauthCallbacksController#passthru (404). Our flow is GET-driven: EntraIdFailureApp returns a 302 to the omniauth path and the browser follows with GET. Fix: set OmniAuth.config.allowed_request_methods = %i[get post] in config/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 autodev

Visit 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

10 Jun 15:00

Choose a tag to compare

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.yml had valid Microsoft 365 credentials. Root cause: config/initializers/load_autodev_config.rb populates Web.config inside Rails.application.config.after_initialize, which fires AFTER every initializer has run. By the time devise.rb reads Web.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.yml directly in devise.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
'