mirror of
https://github.com/mattermost/mattermost.git
synced 2026-11-03 12:48:37 -06:00
cursor/autonomous-agent-workflow-056b
383
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cdf79fef30 | ci: update the test-system-io actions to 18a0baa (#39564) | ||
|
|
bb499f9f93 |
ci(e2e): hand specs that keep failing on master to a repair agent (#38978)
* ci(e2e): hand specs that keep failing on master to a repair agent After each master E2E run, the toolkit's e2e-master-watch reads the run with the triage engine's trunk rules and, for each spec that is broken (failed twice in a row) or flaky, starts the "E2E master repair" Cursor automation with the failing tests, their history and the commits since the last green run. The agent's instructions live in .cursor/automations/e2e-master-repair.md: reproduce on the master images first, fix the cause without masking it, verify the fix several times on its own machine, then open one PR with a single commit, labelled for AI/babysit to follow through. Without the CURSOR_E2E_REPAIR_WEBHOOK_URL/KEY secrets it only writes the job summary. Setting the variable E2E_MASTER_REPAIR to off turns it off. --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
f54f3f765d |
ci(e2e): triage red Playwright and Cypress runs against test history (#38601)
* ci(e2e): triage red Playwright and Cypress runs against test history Each PR lane's status is published by the toolkit's e2e-status action, and a red run is triaged by e2e-triage: flaky or broken on master, the same failure on other PRs, environment errors, or caused by this PR. - e2e-tests-lane-result.yml: one reusable workflow for both Cypress and Playwright that publishes the lane status, triages, refreshes the e2e-test/triage summary, posts the webhook and runs the pass/fail gate. - .github/e2e-status-lanes.json: the one list of PR lanes (enterprise, fips, subpath) that feed e2e-test/triage, read from the workflow's own commit. Master and release contexts carry a suffix and are triaged per lane. - The verified label and the override cover every listed lane. - use-staging picks the TSIO instance, like the TSIO actions. - The subpath lane has its own concurrency key, so it never cancels the enterprise lane. - test_e2e_status_workflows.py checks this wiring. Toolkit pinned to bdce26e. * ci(e2e): pin the toolkit to 24bf208; use the renamed e2e-test-* actions The toolkit renamed e2e-triage to e2e-test-triage and e2e-status to e2e-test-status; inputs are unchanged. Triage now holds a failure repeated on this PR's last commits and never on the base branch as out of date (merge the base branch and re-run) instead of clearing it, and the status line names each kind: flaky, broken on main, caused by PR, out of date, needs investigation. * ci(e2e): pin the toolkit to main (419b0a9) Toolkit #5 merged; this pins its merge on main instead of the PR branch. * ci(e2e): pin the toolkit to main (b3ca677) Toolkit #6 merged: a run flagged as a whole now says why once ("mostly environment errors, re-run" or "many failures in one run") instead of repeating "investigation required". * ci(e2e): re-trigger CI on the update-branch merge head The merge commit created by /update-branch ( |
||
|
|
58b104cac3 |
The rules a new translation is held to (#39299)
* Report a catalog that cannot be read or is not an object, rather than crash A locale catalog whose top level was null, a number or a string made check_icu.mjs throw, and an array was checked as if it were an empty catalog; a catalog it could not read threw too. Either way the run ended at that file, so the catalogs after it went unchecked. Each is now an error against its file, and the rest are checked. en.json is held to the same shape: an array or a number left nothing to check, and every catalog passed. mmgotool i18n verify already reports each of these and carries on. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Keep a copy of go-i18n's server plural forms, pinned by a Go test The translation rules check a server plural map in Node, before the catalog is written, so they need go-i18n's plural categories for each locale. i18n/scripts/lib/server_plurals.json holds them, and a Go test fails if it disagrees with go-i18n for any server catalog, or lists a locale with none. The server translation job runs the test, so a PR that adds a server locale has to add it to the table. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Hold a new translation to the structural rules, and test them in CI i18n/scripts/lib/rules.mjs checks one translation, before it is written to a catalog. Webapp messages go through icu_rules.mjs, the code check_icu.mjs runs, so the two cannot disagree; what CI tolerates as a warning in the catalogs as they stand is an error for new work, except an arrow in a right-to-left locale, which is soft. Server messages get the per-string checks mmgotool i18n verify makes, with plural forms held to go-i18n's table rather than CLDR. icu_rules.mjs gains plural-unused-category, a plural arm the locale never selects. check_icu.mjs does not report it: it is dead text, not something a user sees, so it is left to the pipeline. A new job runs the pipeline's tests, and the workflow now runs for a change under i18n/. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Flag a translation that leaves out a name the glossary keeps in English A doNotTranslate term (Mattermost, GitLab, Markdown) dropped from a translation is a soft finding: usually a mistake, sometimes a sentence that is better leaving the brand implicit, so it lands only with a stated reason. Channels, Playbooks, Boards and Calls are product names that are also ordinary English words. The English reads the same whether a string names the product or starts a sentence about channels, so they are marked commonWord and not flagged; the translator decides from the description. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Catch English left untranslated A translation identical to its English is an error once the English has three ordinary lowercase words, a sentence nobody translated, and soft below that, where it may be a name or a word the language shares with English. In a language not written in Latin script, lowercase English words kept from the source are soft too. Only words a translator would translate count: ICU arguments and tags, template actions, URLs, email addresses, glossary names, quoted values and anything shaped like an identifier are left out. en-* locales are not checked. icu_rules.mjs exports its parser and options, so the rules read a message's literal text the way check_icu.mjs parses it. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Flag the wrong form of address i18n/style.json records, for each locale where the choice matters, how its catalogs address the user, formally or informally, and the pronouns that would mean the other. A translation that uses one is soft: a pronoun can be quoted text or mean someone else, as German "Sie" can at the start of a sentence, so a stated reason lets it land. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Check the glossary and style files in CI The translation rules read i18n/glossary/ and i18n/style.json, so a malformed entry would quietly change what a translation is held to. check_glossary.mjs holds them to their shape: canonical JSON, the known fields only, one owner per term or alias, a glossary file for exactly the locales with catalogs, terms in en.json order, a doNotTranslate term's target the term itself, and no pronoun both used and avoided. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Run every translation check through make i18n-verify-all CI ran the i18n checks as three jobs of inline commands: the webapp checker, make i18n-verify plus the plural-table test in the build container, and the pipeline's tests and glossary check. Nobody could run that set locally without copying it out of the workflow. make i18n-verify-all in server/ now runs all of them, and CI runs only that, in one job on the runner. Go comes from setup-go at mmgotool's go.mod version, as Tools CI does, so the build container, and with it the Docker Hub secrets and id-token permission it needed, are gone. The workflow also runs when server/Makefile changes. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Run make i18n-verify-all in the build environment again The previous commit swapped the build environment for setup-go, for no reason that holds up: the image has the Node .nvmrc pins as well as the Go mmgotool needs, it reuses the Go caches Server CI warms, and it mounts the checkout, so it sees the node_modules webapp-setup installs. Server CI builds and checks in it too. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Drop comments that only restate the code Each said what the line under it already says: a path list, a setup step, a target its help text describes, a function its name and body describe, and how to run tests the Makefile now runs. Comments that give a reason the code cannot show stay. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Fail on a glossary or style file that is not a JSON object readGlossary and readStyle read an array or a number as an empty glossary or no style, which turned the do-not-translate and form-of- address rules off without a word, and threw an unrelated TypeError on null. check_glossary.mjs rejects these shapes in CI, but the rules read the files on their own. Each is now an error naming the file; a file that does not exist still means none. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> |
||
|
|
346c94f0d6 |
Normalize changelog generator output, handle major-version rollover, and keep reviewer edits on reruns (#38478)
* Tighten changelog generator formatting rules and normalize output Refines the SYSTEM_PROMPT in the changelog generator and adds a deterministic post-processing pass so generated entries match the established changelog format without manual cleanup. SYSTEM_PROMPT changes: - State the exact bullet indentation convention: one space for top-level bullets, three for nested ones. The previous instruction asked for two spaces, which did not match the changelog file. - Give the Database Schema Changes subsection an explicit shape: a summary bullet naming the release, with each individual change nested beneath it. - Extend the config.json block with a removed-setting example and spell out that removed settings are top-level bullets rather than nested under a plan grouping. - Show the Improvements section verbatim so the blog post line lands immediately after the heading with no blank line between them. - Strengthen the prohibition on horizontal rules, which break the MDX document, into an explicit NEVER in both the section-structure and markdown-formatting rules. Add normalize_formatting(), applied to the model output before the entry is assembled. The prompt specifies all of these, but compliance is not guaranteed and each mistake produces a visibly wrong diff: - strips standalone horizontal rules - reduces even bullet indents by one (2 -> 1, 4 -> 3) - closes a blank line between ### Improvements and its blog post line - collapses the blank-line runs left behind by rule removal Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nrmq4ezefYWqa9YkxtX3mR * Escape MDX-unsafe characters in generated changelog prose The changelog is an .mdx file, so an unescaped `{` opens a JSX expression and `<` followed by a letter, `/` or `!` opens a JSX element. Release-note prose containing text such as `<plugin ID>` or `{"status": "ok"}` therefore breaks the docs build. The SYSTEM_PROMPT already instructs the model to wrap such text in backticks, but compliance is not guaranteed and the failure mode is a broken build, so escape defensively. Add escape_mdx_unsafe(), applied to the model output after normalize_formatting(). It backslash-escapes bare `{` and bare `<` when followed by a letter, `/` or `!`, while copying protected regions verbatim: inline code spans (double- and single-backtick) and Markdown link destinations. A negative lookbehind avoids double-escaping text that is already escaped. Closing `}` is deliberately left alone - escaping the opening brace is sufficient to prevent a JSX expression, and this keeps the generated heading anchor intact. Escaping runs before BLOG_POST_LINK substitution and Go Version injection, so the Markdown link and the backticked Go version are inserted after the escape pass and are never touched by it. Also harden normalize_formatting(): its horizontal-rule removal would strip the `---` delimiters of a YAML frontmatter block, which MDX requires at the very start of a file. A leading frontmatter block is now split off and restored verbatim, so the function is safe if it is ever applied to whole file contents rather than just the generated fragment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nrmq4ezefYWqa9YkxtX3mR * Update generate_changelog.py * Update generate_changelog.py * Update generate_changelog.py * Update generate_changelog.py * Update generate-changelog.yml * Update generate_changelog.py * Derive the changelog path in the script and guard version/milestone Two gaps remained in the major-rollover handling: The workflow resolves the changelog file from the version, but the script still fell back to a fixed "CHANGELOG.md" when CHANGELOG_PATH was unset, so a direct invocation would append a v12 entry to the repository-root changelog with nothing to warn about it. changelog_path_for_version() derives the default from VERSION instead, and major_version() now raises on a version it cannot parse rather than returning an empty string. The script prints the file it targets so a wrong path is visible in the run log. Nothing validated the two dispatch inputs against each other. The milestone selects which PRs are collected and the version selects both the entry heading and its file, so a mismatch files one release's notes under another's. The workflow now compares their major versions in its first step and fails before anything is generated. It compares majors only, so a deliberate v11.10.0/v11.9.0 pairing still runs. insert_changelog_entry() also creates the parent directory when writing a file for a new major that does not exist yet. Verified: the derived path for v11.9.0/v12.0.0/12.0.0 and the raise for malformed versions; bootstrapping mattermost-v12-changelog.mdx into a missing directory writes the frontmatter header before the entry; a second insert lands after that header and above the first entry; the workflow parses and the guard is the job's first step; the guard's shell logic accepts matching majors and rejects mismatched ones. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014gJFVcJtw4rdpjWrmpP9T7 * Rank bullet indents per list block in normalize_formatting Rule 2 took a single minimum indent across the whole fragment and treated anything deeper as nested. That had two failure modes: - Sections are generated independently and can disagree on base indent. A fragment with one section at indent 0 and another at indent 1 gave base 0, so the depth-1 bullets — already correct top-level — were pushed to three spaces and rendered as nested. - A three-level list collapsed to two, because every indent deeper than the minimum mapped to the same three spaces. Indents are now ranked within each list block, mapping level n to 1 + 2n spaces. Blocks are delimited by lines that are neither bullets nor bullet continuations, so each section resolves its own levels, and ranking keeps a third level distinct instead of flattening it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR6hsSCuRfXqAnyHNY7sKo * Add a major release type and guard against truncated changelog output Three fixes on top of this branch, all of which v12.0 exercised in practice. release_type had no "major" option. The dispatch input offered only "feature" and "esr", so generating v12.0 emitted "Feature Release" and the anchor #release-v12-0-feature-release. Both had to be corrected by hand, and four docs pages deep-link to #release-v12-0-major-release. "major" is now a third choice, and the anchor slug is derived from the heading label instead of being written out separately, so the two cannot drift apart. A vX.0 release generated under any other release type now prints a warning naming the heading and anchor it is about to produce. Truncation. polish_with_ai capped output at 4096 tokens and never looked at stop_reason, so a response that overran the cap was silently cut mid-word — the v12.0 entry is ~32k characters, roughly twice the cap. The cap is raised to 16384 and a max_tokens stop reason is now a hard error. As a backstop for truncation arriving any other way, check_for_truncation rejects an entry containing a bullet that ends without terminal punctuation, before anything is written. It allows the endings that legitimately occur — a bare inline-code span, a fully bold label, a Markdown link or parenthetical, a bare URL, and a bullet continued on the next line — and flags nothing across the 2,702 bullets in the existing v10, v11 and v12 changelogs. Identifier fidelity. The same run shortened ``/ephemeral_mode/cleanup`` to ``/cleanup``. A prompt rule now requires endpoint paths, setting names, commands, table and column names, and feature flags to be reproduced in full. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR6hsSCuRfXqAnyHNY7sKo * Run the truncation check only on AI-polished prose Applying it to the no-API-key fallback would block legitimate runs: that path emits PR authors' raw release notes verbatim, and those routinely end without a full stop. Across the 71 release notes on the v12.0.0 milestone, 4 do — "Fixed deeplink routing" among them — none of which is truncated. polish_with_ai now reports whether it actually polished, and the check runs only when it did. The AI output itself is reliably punctuated: the 188 bullets of the generated v12.0 entry all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR6hsSCuRfXqAnyHNY7sKo * Keep reviewer edits on changelog reruns and record each bullet's source PRs A rerun rebuilt the changelog branch from master and force-pushed it, discarding every edit made in the PR, and then failed at gh pr create because the PR already existed. That rules out running the generator repeatedly while a release is in review. - Add a mode input. auto (default) updates an open changelog PR in place: only notes from PRs its body does not list yet are generated, and they are inserted into the matching sections of the existing entry as new commits, never a force-push. With no open PR it creates one. regenerate keeps the old rebuild-from-scratch behaviour on request. dry_run generates only, with nothing pushed. - Open the PR as a draft. - Tag each note with an ID and have the model mark every bullet with the IDs it used. Markers are stripped before writing, and the attribution goes to sections.json (bullets per section with their source PRs) and to a changelog-sources comment in the PR body, which is how the next run tells new PRs from ones already included. - Upload entry.mdx, sections.json and pr_body.md as an artifact and show the generated text in the run summary. - Run the generator from the workflow's own checkout, since the PR branch may carry an older copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DR6hsSCuRfXqAnyHNY7sKo * Keep changelog reruns inside the list and refuse unattributed notes Update mode appended new config bullets after the upgrade admonition and did not recognize two-space plan labels, so a rerun could split a section or repeat a plan heading. New bullets now stop at an MDX admonition, and top-level is the shallowest indent already in that section. A note with no source marker is no longer recorded as included, because the next run would skip it. Multi-line raw notes keep their marker, and star or plus bullets are rewritten to dashes so update mode does not drop them. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Eva Sarafianou <eva.sarafianou@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
46ed11842e |
Stricter i18n catalog checkers (#39275)
* Fail an empty translation even under the warn-missing flags CI runs both checkers with --warn-missing-keys / --warn-missing-ids so a catalog can lag en.json. That was also downgrading an empty value to a warning, so a "" placeholder left in a catalog would merge. An absent key is work in progress; an empty one is a leftover placeholder, and now always fails. * Check each webapp catalog's formatting A hand edit that reindents or reorders a catalog buries its one real change in a diff of thousands of lines, and nothing caught it. check_icu.mjs now requires each catalog to be its own canonical serialization: keys in code-point order, which every webapp catalog already has, a 2-space indent and a trailing newline. A duplicate key fails too, since JSON.parse keeps only the last one. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Check each server catalog's formatting A hand edit that reindents a catalog buries its one real change in a diff of thousands of lines, and nothing caught it. mmgotool i18n verify now requires each catalog to be its own canonical serialization: a 2-space indent and a trailing newline. The server catalogs have no order, so entry order is left alone. A duplicate id fails, which the loader silently collapses. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Warn about horizontal arrows in right-to-left locales Arrows are not bidi-mirrored, so in a sentence read right to left an arrow meaning "next" or "leads to" points backwards, and nothing checked for it. check_icu.mjs now warns when a right-to-left locale's translation contains a horizontal arrow or pointer. The locale's direction comes from Intl, so no locale is listed by hand. Vertical arrows and the ↵ key symbol read the same either way and are not flagged. * Warn about horizontal arrows in right-to-left server catalogs too check_icu.mjs warns when a right-to-left locale uses an arrow that is not bidi-mirrored. mmgotool i18n verify now does as well, for single and plural translations. It reports the one such string fa.json ships today. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Stop using -- as a dash in the checkers' comments Both checkers take --flags, and a -- dash beside them reads as one. * Move check_icu.mjs's per-message rules into icu_rules.mjs check_icu.mjs held the file-level checks and the per-message ICU rules in one script, so nothing else could run exactly the rules CI runs. The per-message rules now live in icu_rules.mjs: analyzeSource parses an en.json message once, and checkMessage returns each finding with a stable rule id and a severity. check_icu.mjs keeps the file-level checks and reports each finding as before. The output over the shipped catalogs is byte-identical before and after, with and without --warn-missing-keys. The i18n CI workflow now also runs when icu_rules.mjs changes. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Warn about plurals missing a form their locale needs A plural that shows its count but lacks a form its locale needs for some integer renders that count with "other": Russian ships "5 каналы" where it should say "5 каналов". check_icu.mjs now warns about it. Explicit =N arms count as covering N. Forms that only fractions or numbers in the millions select are not required. A source plural that itself leaves out a form English needs is not held against the translations that copy it. When two plurals on one variable fail the same way, the finding is reported once. It is a warning because 370 shipped strings have the problem, nearly all in pl, cs, uk, ru, lt and ro. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> * Show "this user" for one user in two zh-CN removal prompts Chinese never selects the plural category one, so the one {这位用户} arm was dead and a single user read as 这些用户 (these users). =1 selects exactly one. Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> --------- Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
f292f95d5a |
ci(docs-preview): avoid spurious failures from superseded runs (#39123)
* ci(docs-preview): stop rebuilding on ready_for_review * ci(docs-preview): don't report failure when a run is superseded |
||
|
|
491e853f7b |
Monthly maintenance (2026-10): Update server dependencies (#39220)
* Update server Go dependencies for October 2026. Bump module dependencies within their current major versions, including golang.org/x/net v0.61.0 which fixes the HTTP/2 issues addressed in v0.60.0. Co-authored-by: jesse <jesse@mattermost.com> * Retrigger Server CI after Postgres shard runner kills. Co-authored-by: mattermost-code <matty-code@mattermost.com> * Hold back logr v2.0.22 and azblob v1.8.0. logr v2.0.23 defaults to a sync.Map level cache and pre-fills 65535 entries on every clear, which OOMs fully parallel app/api4 tests. azblob v1.8.1+ pulls apache/arrow-go into every filestore binary. Co-authored-by: mattermost-code <matty-code@mattermost.com> * Upgrade azblob to v1.8.2. Keep list-blobs on the SDK default format. The module links arrow-go for the opt-in Arrow parser. Co-authored-by: jesse <jesse@mattermost.com> * ci: make e2e dispatch-run job names unique per chain --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: jesse <jesse@mattermost.com> Co-authored-by: mattermost-code <matty-code@mattermost.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
b0ddc5a13a |
Add Conflicts Babysit workflow to dispatch conflicting PRs to a Cursor agent (#39272)
* Add Conflicts Babysit workflow to dispatch conflicting labeled PRs to Cursor On every push to master, find open PRs labeled AI/ConflictsBabysit whose mergeable state is CONFLICTING (retrying briefly while GitHub reports UNKNOWN) and POST each one to the Cursor automation webhook. Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Remove workflow_dispatch trigger from Conflicts Babysit Manual dispatch can target any branch, which would run that branch's version of the script with the webhook secrets. Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Make Conflicts Babysit best-effort so it never fails master CI Webhook and PR search failures are reported as warnings and in the job summary instead of failing the run; checkout and dispatch steps are continue-on-error as a backstop. Co-authored-by: Maria A Nunez <maria.nunez@mattermost.com> * Report Conflicts Babysit failures hidden by continue-on-error Skip dispatch when checkout fails, add a step that warns and writes the job summary when checkout or dispatch fails, and write the job summary when the PR search fails. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
d430bb294f |
SEC-11251 Add Playwright subpath deployment E2E testing (#38833)
* Add Playwright subpath deployment E2E testing * Address CodeRabbit findings: subpath CI trigger gap, container cleanup scope, internal webhook URL * Widen subpath CI trigger to cover the full deployment configuration * Also trigger subpath E2E tests on reusable Playwright workflow changes * Also trigger subpath E2E tests when the goto/baseURL fixture chokepoints change * Fix defaultBootEnv() dropping subpath SiteURL on per-spec-file reset ensureDefaultServerEnv() restarts the server with defaultBootEnv() before each spec file's first test. defaultBootEnv() didn't know about subpath mode, so it reset SiteURL back to the plain default and broke every subpath spec after the first server restart. Co-authored-by: saturnino <saturnino@mattermost.com> * Fix baseURL on mid-test restart in subpath mode; widen CI path filter restartMattermostContainerLocked() always reset baseURL to the direct server URL, dropping the subpath prefix after any mid-suite restart (e.g. via ensureFeatureFlag()). Keep it pointed through nginx when subpathMode is set. Also widen the SHOULD_RUN_SUBPATH file filter to cover util.ts and the channels/system_console page objects, which subpath specs depend on. Co-authored-by: saturnino <saturnino@mattermost.com> * fix specs that broke when the server runs behind nginx at /subpath: - Add toHavePath/toHaveHrefPath matchers that prepend the subpath, and lint against toHaveURL('/path'), which Playwright resolves against baseURL and so drops the subpath - Route unprefixed API calls onto the subpath, point mmctl at the subpath-aware URL, and build permalinks from the site URL - Auto-start nginx whenever PW_SERVER_DEPLOY=subpath - Skip the OIDC/SAML error-page cases (signature mismatch under a subpath) and the MM-69979 post_height cases for the subpath project - Wait for the phase2 permissions migration before the autotranslation scheme tests - Fix the mobile schedule menu dimmed-area click when the dialog starts above the viewport, and pin the date separator rollover test to UTC * keep the subpath SiteURL reachable from the server --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: saturnino <saturnino@mattermost.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
356bdc7d9b |
docs: scope AI documentation drafts to the merged change (#39211)
* docs: scope AI documentation drafts to the merged change Draft PRs were titled by number, filled with the review loop, and restyling whole pages because pre-open review treated every line as new. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: strip HTML comment delimiters from AI draft summaries An unmatched comment opener in the model summary is an HTML block through the end of the pull request body, which hides the editorial-review notice. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: drop a draft summary that does not match the accepted revision An accepted revision with no summary fence was keeping the previous pass's summary, so the draft pull request could describe files that had already been replaced. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: match sibling spacing when a draft adds a section The command reference puts each label on the line above its fence. Drafts were inserting a blank line there, which made a new command harder to scan than the rest of the page. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: let related brief actions share a section Forcing a heading per gap-brief action split behaviours that belong together on the page. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: keep the writer trail and the full action list The pre-open trail disappeared with the runner, and a maxed gap brief could drop the last action before review. Co-authored-by: Cursor <cursoragent@cursor.com> * docs-ai: use git diff --no-index for pre-open review diffs Co-authored-by: Cursor <cursoragent@cursor.com> * docs-ai: keep prior draft when a revision reverts the page; fold draftTitle into author-loop Co-authored-by: Cursor <cursoragent@cursor.com> * docs-ai: never draft changelogs, upgrade notes, or the version archive Co-authored-by: Cursor <cursoragent@cursor.com> * docs-ai: keep gap analysis recommendations unchanged Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
4f932a4519 |
fix(docs-ai): draft pages when the source PR has no milestone (#39126)
A missing release should leave the human-judgment version anchor for an editor instead of aborting the docs writer. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
38ff39c323 |
Fix config change checker false release notes after PR base retarget (#38985)
* Stop attributing default-branch config/API diffs when a PR base is retargeted. The checker used git diff BASE...HEAD, so retargeting onto an older branch made every watched file already on master look like part of the PR. Only commits that are not on the default branch are considered now. Co-authored-by: Cursor <cursoragent@cursor.com> * Give the cherry-pick fixture a distinct release parent. On CI, cherry-picking onto the same parent as master produced an identical SHA, so --not master dropped the commit and the test failed. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
c57df45d9c |
docs: self-host fonts, images, and OpenAPI icons; block build egress (#39092)
* docs: self-host fonts, images, and OpenAPI icons; block build egress Stop hotlinking GitHub/user-images and Google Fonts on the docs site, replace embedded video iframes with links, and fail CI when a build would fetch off-origin resources (closed allowlist for the security-updates feed). Co-authored-by: Cursor <cursoragent@cursor.com> * docs: address CodeRabbit egress review findings Scan an offline (no-Algolia) rebuild in Docs CI, tighten ATTR/getAttr boundaries, flag remote template-literal prefixes before ${, and add alt text for the self-hosted plugin-management image. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: replace egress fixture tree with in-memory unit tests Keep scanner coverage without committing dozens of tiny HTML/CSS/JS fixture files. scanSources takes string maps; CLI smoke uses a temp dir. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: keep YouTube/Wistia embeds on hosted site Restore in-page players for the public docs. Allowlist those hosts in check-egress; the offline packager strips them for airgap. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: simplify hosted-embed allowlist to base domains Co-authored-by: Cursor <cursoragent@cursor.com> * docs: tighten hosted-embed allowlist to player kinds and paths Only iframe/script/img player URLs pass; fetch() and other loads to YouTube/Wistia hosts still fail egress. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: allow Wistia swatch preload in hosted-embed egress check The organize-conversations embed emits link[rel=preload] for the swatch; kind-scoped allowlisting missed it and failed Docs CI. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: use getAttr boundary for script hasSrc check \b matches inside data-src, so the old hasSrc could skip scanning an inline script body. Align with ATTR/getAttr and cover the case in tests. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
e32d8734cb |
Request docs-draft review from a human when the source PR author is a bot (#38987)
* When a bot authors the source PR, request docs-draft review from a human reviewer of that PR instead of the bot. Co-authored-by: Cursor <cursoragent@cursor.com> * Keep the docs draft running if GitHub reviewer lookup fails. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
0f35dda92b |
Gate rolling-upgrade E2E on master/release merges by changed files (#38873)
Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
5427f6b024 |
Draft docs from merged Docs/Needed via author personas (#38764)
* docs: draft pages from merged Docs/Needed via author personas When a PR merges still carrying Docs/Needed, read the gap sticky comment, author MDX with the matching audience personas (pre-reviewed in-loop), and open docs/pr-<n>. Closing that draft flips Docs/Needed to Docs/Done. Retires the cross-repo docs-needed.yml workflow. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-ai): harden writer fence parse, version check, and brief fallback Match CodeRabbit findings that still hold: outer fences of equal length so nested code samples survive; require the milestone version (not any vX.Y); fall back to PR evidence when the gap sticky is missing; narrow the App token. Keep pull_request (not pull_request_target) to match the gap pipeline's fork posture. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-ai): tighten author path scope, ERROR halt, and sync provenance Restrict each author pass to its group targets; fail the loop on review ERROR instead of opening a half-reviewed draft; require same-repo head and DOCS_AI_BOT_LOGIN before flipping Docs/Done. Also cover additionsDiff edge cases and longest-prefix author matching. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: lift observability and parity guidance into docs-writer prompt Carry the useful bits from draft_docs.md without restoring triage: parity vs net-new as a one-line scope rule, plus when to document logs/metrics. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-ai): require author passes to cover every group target Emitted ⊆ allowlist was not enough — an incomplete pass could drop a named page. Reject initial omissions; keep prior groupFiles when a revision fails to re-emit every target. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-ai): harden revision limit, impact paths, and anchor reject Validate DOCS_AI_MAX_REVISIONS as a non-negative integer; filter gap impact locations to allowed .md/.mdx before grouping; keep the prior pass when a revision drops the milestone version anchor. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-ai): drop AI-Drafted label; frame unknown versions Identify drafts by App authorship instead of Docs/AI-Drafted. Require "From Mattermost [NOT PRESENT — …]" for unknown releases, and replace the DEBUG observability example with admin-facing cases. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-ai): address writer review on sync, paths, and author context Require draft merge before Docs/Done, filter deny-listed paths at extraction, prefer branch provenance, pass existing pages into the author prompt, and tighten workflow input/credential handling. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-ai): keep full page context on author and revision passes Reject oversized pages instead of truncating them, feed the in-memory draft into revision prompts, and tighten line-based MDX import/export checks. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
27c501d217 |
Replace docs impact review with self-clearing gap detection (#38177)
* Replace docs impact review with self-clearing gap detection The old workflow sparse-checked-out mattermost/docs and cited source/**.rst paths that no longer exist, so it was judging the current PR against a docs tree this repo replaced. It also applied Docs/Needed and never removed it, which made the label a permanent record of a gap rather than a statement about the PR as it stands. docs-gap.yml reads both sides of the diff — the code and whatever docs the PR already carries — so a later push that documents the change flips the verdict to none and clears the label. The label follows the verdict, not the presence of a docs diff, or clearing it would be an escape hatch anyone could take by editing an unrelated page. Prior state travels in the sticky comment, so a label a human applied survives a none verdict. The audience personas come from the same frontmatter the review router reads instead of being restated in workflow YAML, which is what keeps the two halves of the pipeline from disagreeing about who reads what. Both are added and removed in one commit: they share a trigger, so running them together would double-comment and race the label. Co-authored-by: Cursor <cursoragent@cursor.com> * Tune docs-gap prompt, CAP limits, and Action pins Correct API docs publish scope, size wrap caps for ~10k/1k LoC and ~100 paths, and bump checkout/setup-node to v7. Co-authored-by: Cursor <cursoragent@cursor.com> * Split docs-ai into review/, gap/, and shared lib/ Make which pipeline owns which scripts obvious; keep one package and shared helpers. Co-authored-by: Cursor <cursoragent@cursor.com> * Trim docs-gap comments to non-obvious rationale Keep the why for label lifecycle, soft-fail, and secrets gating; drop restated narration. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix gap-report footer, label-remove restore, and cancelled report job Address CodeRabbit: hide opt-out on none, restore sticky if removeLabel fails, shell-safe dry-run example, skip report on concurrency cancel. Co-authored-by: Cursor <cursoragent@cursor.com> * Harden sticky author match and pin claude-code-action v1.0.235 Match stickies to github-actions[bot] only, use the job GITHUB_TOKEN instead of Claude App OIDC on analyze, and bump the action pin. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
8bfa298a54 |
[E2E/Playwright] Add password, MFA, authentication-method, and claim tests (#38520)
* Add Playwright password, MFA, authentication-method, and claim tests Adds password requirements, MFA visibility/enforcement, System Console authentication-method column, and authentication-transfer claim coverage. Deletes the ported Cypress specs. Locates the user row by exact username instead of assuming index 0, and waits for the users-report response matching the search term before reading it. Anchors the password-length error locator so it can't match the longer complexity message, and asserts the length reset to the compiled default (5). MM-T1778 skips when testcontainers isn't available, since its enforced-MFA cleanup depends on the local mmctl path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Fix Cypress override-setting test pollution and gh api --slurp/--jq CI script bug - helpers.js: make enableUsernameAndIconOverrideInt idempotent so a sibling spec leaving EnablePostUsernameOverride/EnablePostIconOverride already enabled doesn't leave #saveSetting permanently disabled (30s timeout in disallow_username_profile_override_spec.js). Replace the spec's dead mattermost.com icon fixture URL (404) with a stable in-repo asset. - e2e-tests-override-status.yml / e2e-tests-verified-label.yml: gh api rejects --slurp combined with --jq in one call; split into a pipe to a separate jq invocation instead. - e2e-tests-playwright.yml: bump playwright-full workers 20 -> 30. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
3a1f5f0da1 |
Add AI persona review pipeline for docs PRs (#38144)
* Add AI persona review pipeline for docs PRs Six reader personas review changes under docs/main, docs/develop and docs/api, and one sticky comment per PR carries their verdicts. Advisory only: nothing blocks a merge, and the review is a no-op when ANTHROPIC_API_KEY is unset, so forks and unconfigured checkouts are unaffected. The registry is the set of files in .github/prompts/personas/, each carrying its metadata in frontmatter, so adding a persona means adding a file. Rules that don't vary by audience live in conventions.md and review-contract.md instead of being restated six times, and they form a byte-identical prefix across personas so they hit the prompt cache. Conventions were derived by measuring docs/ rather than ported from the migration PoC, which corrected three wrong assumptions: title is the only required frontmatter key, plan gating is <PlanAvailability slug>, and internal links are absolute site paths. No prose linter runs. The Vale config already in the repo comes from the PoC and has never been run against this corpus; it produces ~8,375 findings, most of them false positives, so brand-voice judges heading case and terminology in context instead. This change leaves those files untouched and records the measurement in the docs-ai README, so whether they should stay at all can be decided separately. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix version anchoring rules and drop the milestone from docs review The milestone was a no-op. Assigning one is not a workflow trigger, and the value comes from the event payload, so a milestone set late in the cycle — which is when release management sets them — never reached a review at all. Removed end to end: the workflow env var, the script flag, and both prompt branches. Authoring keeps its milestone rule, since docs-needed.yml guarantees one is set before the writer runs. Version anchoring is now split by role rather than by who wrote the change. A reviewer cannot tell whether a person or a generator produced a diff, so the previous framing was undecidable at the point of use. Reviewers check that an anchor exists, accept one the author states — developers ship docs with the code and know their target release — and always flag the authoring placeholder, which only a human can clear. Reviewers also no longer defer findings. The old budget guidance had brand-voice drop lesser categories to fit three slots, which surfaced them one push at a time as earlier ones were fixed, indistinguishable from the standard moving. Findings are pattern-collapsed instead, and a truncated list names its remainder in the summary so an author can fix everything in one pass. Drops dry-run.mjs and lib/diff.mjs, which nothing else consumed, and the README link from the comment footer. Comments trimmed throughout. Co-authored-by: Cursor <cursoragent@cursor.com> * Tighten docs AI prompts and harden the review comment Scope brand-voice's consistency check to the diff: it cannot see pages outside the change, so asking it to compare against neighbours invites claims about content it never read. Only adopt a bot-authored comment when updating the review. The marker renders invisibly, so anyone able to comment could plant it and have the next run overwrite their post. Make the verdict example valid JSON, replace the frontmatter regex with an explicit split, and cut the README back to the persona format and the constraints a contributor can unknowingly break. Co-authored-by: Cursor <cursoragent@cursor.com> * Validate persona docs_paths and code_signals at load A docs_paths entry that no longer exists matches nothing, so the persona silently stops being selected for the content it owns; an impact-scoped persona with no code_signals silently gates on nothing. Both were only caught by the test suite. Check them where the registry is built, so editing a persona locally fails with the offending path named. Code paths are deliberately not checked on disk: they are matched as prefixes and move far more often than docs directories. Also say in the README that the file body is the persona's system prompt. Co-authored-by: Cursor <cursoragent@cursor.com> * Test the model-output and frontmatter edge cases parseJson and withRetry had no coverage at all, and they sit either side of every model call: one turns fenced or prose-wrapped output into a verdict, the other decides whether a failure costs one API call or three. The frontmatter rules were covered only for the personas we ship, so the validation itself — id mismatch, unknown scope, a docs_paths entry that does not exist — went unexercised. Two seams were needed. parsePersona now takes the file contents instead of reading them, because fixtures cannot live in the personas directory: that directory is the registry, so a fixture in it would be a reviewer. withRetry takes an injectable wait, so the retry path is tested without 3.5s of real backoff. Each new test was checked by mutating the code it covers. Co-authored-by: Cursor <cursoragent@cursor.com> * Validate the persona registry on master, and co-locate the unit tests The registry resolves docs_paths against the working tree, so a docs rename that lands between a PR's last run and its merge breaks master with neither PR going red. Run validate on push as well; prepare now gates on the event, and review and report follow it, so a push costs one job and no model calls. Move the three unit tests beside the modules they cover, matching how the webapp places its tests. registry.test.mjs stays at the top level: it checks the personas and prompts we ship rather than any one module. Co-authored-by: Cursor <cursoragent@cursor.com> * Find the sticky review comment past the first page of a PR thread report.mjs read one page of issue comments and stopped. That endpoint returns oldest-first and takes no ordering parameter, so a sticky comment created once a thread already held 100 comments fell outside the only page ever fetched, and every later push posted a duplicate instead of editing it. The GitHub client moves to lib/github.mjs, where the comment walk is a lazy generator that stops at the first match: unchanged single request on a short thread, and no duplicates on a long one. Also drops docs/api from review scope. Its reference pages are generated from the OpenAPI spec, so a finding there can only ever be fixed in api/v4/source/, and the workflow, the personas and the README now say so. The workflow excludes docs/main/agents/docs for the same reason. complete() takes userPrompt rather than user, so the argument reads as the prompt it is next to the system prompt. Co-authored-by: Cursor <cursoragent@cursor.com> * Run docs persona review on pull requests only The push trigger only ever ran `validate`; the sticky comment the rest of the workflow exists to produce has nowhere to land outside a PR. A registry break now surfaces on the next docs PR instead of on the merge that caused it, which costs a little latency and saves a run on every merge to master. Co-authored-by: Cursor <cursoragent@cursor.com> * Run docs AI review via workflow_dispatch as a single job The pull_request matrix attached a check per persona plus validate / prepare / report, which flooded the PR checks list before we had tuned the personas. Manual dispatch with a PR number lets us exercise real diffs without an audience; collapsing to one job keeps the later pull_request restore to a single check. Scripts and prompts come from the workflow ref, so a same-repo branch cannot rewrite the pipeline that reviews it. Also bump @anthropic-ai/sdk to 0.127.0 (was 0.40) and js-yaml to 4.3.2 for the merge-keys advisory, and disable the SDK's own retries so they do not stack on withRetry. Co-authored-by: Cursor <cursoragent@cursor.com> * Trim docs-review workflow comments to the non-obvious Keep only why dispatch is manual/single-job, why PR metadata avoids GITHUB_OUTPUT, the merge-ref fallback, fail-soft persona waits, and why SDK retries are disabled. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
9026858eb6 |
docs-ime-render: replace npx wait-on with curl poll (#38787)
Follow-up to #38782. The workflow's server-readiness step used `npx --yes wait-on`, which fetched the latest wait-on tarball from npm on every run — non-reproducible, and any compromised release of wait-on or its transitive deps would run in CI with GITHUB_TOKEN access. Adding wait-on as a pinned devDep would fix reproducibility but pull 21 transitive packages (axios, joi, rxjs, @hapi/joi stack) into package-lock.json just to poll an HTTP endpoint. Replace it with a one-liner curl retry loop instead — curl ships on every runner, no npm fetch, no lockfile change, same 10-minute cap. Reported by @NARSimoes on #38782. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d584e92645 |
docs(site): render IME diagram PNG on PRs that touch it (#38782)
* Add docs-ime-render workflow Regenerates the rendered PNG snapshot of the IME diagram (docs/site/static/img/ime/entadv.png) on merges to master when the IMEDiagram component, its content modules, or its logo/banner assets change, and commits the result back to master. The live docs site renders <IMEDiagram /> at page-view time, so this PNG only exists for external use (marketing decks, PR descriptions, emails) where a live React component can't be embedded. Being committed and shipped by docs-cd means it lives at https://docs.mattermost.com/img/ime/entadv.png — a stable URL that survives branch deletion, unlike raw.githubusercontent.com links. Trigger paths cover everything the render depends on; the auto-commit only touches static/img/ime/entadv.png, which is not in the trigger paths, so the workflow cannot self-recurse. Concurrency group serializes runs so a rapid sequence of merges collapses to one final committed PNG. workflow_dispatch allows the first render (and any manual re-render) without needing an eligible source change. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-ime-render): use npm start so prestart fires npx docusaurus start bypasses the npm 'prestart' lifecycle, so the sidebars, OpenAPI reference, and plugin docs are never generated. Docusaurus then crashes at boot on a missing require in sidebars/api.ts and wait-on times out. Switch to 'npm start -- --port ... --no-open' so prestart runs first. Bump the wait-on timeout to 10 minutes to cover prestart (OpenAPI + plugin godocs) plus Rspack's cold compile. Co-authored-by: Cursor <cursoragent@cursor.com> * docs-ime-render: submodule checkout + first-run PNG detection - Enable 'submodules: true' on checkout. prestart runs stage-agents-docs.mjs which needs the docs/vendor/mattermost-plugin-agents submodule; without it, Docusaurus fails to boot. Matches the setup in docs-cd and docs-ci. - Fix the commit-if-changed step to detect an untracked PNG (the first-ever render): stage the file first, then diff --staged. The previous 'git diff --quiet' only saw tracked changes and silently no-op'd on first run. - Drop the '[skip ci]' commit trailer. GITHUB_TOKEN pushes never trigger workflow runs regardless of the trailer, and the trigger paths already exclude the PNG output so this workflow can't self-recurse. The trailer was cosmetic and misleading. Co-authored-by: Cursor <cursoragent@cursor.com> * trigger CI Co-authored-by: Cursor <cursoragent@cursor.com> * chore(docs): refresh rendered IME diagram Auto-generated by .github/workflows/docs-ime-render.yml. * render-ime: widen viewport + hide site chrome before screenshot Two visible bugs in the CI-produced PNG: - Viewport 1280x900 minus the docs sidebar left the diagram wrapper below its 900px container-query breakpoint, collapsing the whole component to a 2-column mobile layout. Bump viewport to 1728px so the wrapper lands at its 1240px max-width. - Playwright element screenshot captures whatever pixels sit inside the element's bounding box, including position: sticky chrome that overlays it during scroll. When the diagram was tall (2810 CSS px after the mobile collapse), the Docusaurus navbar bled into the middle of the PNG. Inject a small style tag before screenshot that hides the navbar, sidebar, TOC, footer, and pagination-nav. The style injection is scoped to this render only (page-level, not persisted), and the export URL flag (?imeExport=1) is unchanged. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(docs): refresh rendered IME diagram Auto-generated by .github/workflows/docs-ime-render.yml. * docs-ime-render: drop rationale comments from workflow Design context now lives in the PR description; the workflow only keeps the top-line 'what this does'. Co-authored-by: Cursor <cursoragent@cursor.com> * docs-ime-render: bump actions to latest majors - actions/checkout v6.0.2 -> v7.0.1 - actions/setup-node v6.3.0 -> v7.0.0 - actions/setup-go v6.3.0 -> v7.0.0 Co-authored-by: Cursor <cursoragent@cursor.com> * docs-ime-render: drop unused perm + scope push token - Remove pull-requests: read (workflow never calls the PR API) - persist-credentials: false on checkout so GITHUB_TOKEN isn't left in .git/config where npm ci and postinstall scripts from PR-controlled deps could read it - Push step sets its own token-scoped remote URL for the single command Addresses review from @NARSimoes on #38782. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |
||
|
|
86ee66a92d |
Add workflow to auto-label docs PRs for editor review (#38717)
* Add workflow to auto-label docs PRs for editor review Apply the existing "2: Editor Review" label when non-draft, same-repo PRs change files under docs/, so technical writers are notified without relying on the disabled docs impact review workflow. Co-authored-by: Cursor <cursoragent@cursor.com> * Update .github/workflows/docs-editor-review-label.yml Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update .github/workflows/docs-editor-review-label.yml Co-authored-by: Eva Sarafianou <eva.sarafianou@gmail.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Eva Sarafianou <eva.sarafianou@gmail.com> |
||
|
|
6ee80ce049 |
Temporarily disable Documentation Impact Review workflow (#38659)
The mattermost/docs content has been migrated into the monorepo, but this workflow still checks out and analyzes the legacy mattermost/docs RST tree. Switch the trigger to workflow_dispatch so it no longer runs on every PR until it is rewired to the in-repo documentation. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
7c55cb93c2 | Stop package-lock.json from losing its peer markers (#38265) | ||
|
|
fe9a36e80e |
E2E/Playwright: rolling upgrade tests (#38367)
* E2E/Playwright: keep server state across a container swap `/mattermost/config` and `/mattermost/data` are Dockerfile VOLUMEs, so replacing the container factory-reset the config and dropped uploads. Store the config in Postgres and bind-mount the data directory to `.mattermost_data/` instead. The server writes there as UID 2000 into 0750 dirs, so on Linux the host user cannot delete them: resetting on a fresh boot threw EACCES and failed global setup on every second boot, and so on every retest. Clear from a container when the host cannot. Adds the library pieces the upgrade projects need — image swapping, phase log capture with boot env redacted — and fixes the specs this exposed. * E2E/Playwright: add the upgrade-path harness The suite only ever ran against a server booted fresh on the new image, so an upgrade-only regression would surface first in a customer's upgrade. Three projects perform an in-place upgrade: `upgrade-from` seeds an older image and records a baseline, `upgrade-swap-to` replaces only the server container, `upgrade-to` re-verifies the baseline. API-first, since older images ship older web apps. From-versions come from the published support policy at run time — the last three minors plus any active ESR, strictly older than the version under test. * E2E/Playwright: run rolling upgrades in CI, one status per from-version An entry workflow resolves the from-version matrix and calls a template once per version, in parallel. Each run posts its own commit status, so a failure names the version it came from; an empty matrix posts `upgrade-from-none`. Every worker upgrades its own server, then draws from the full suite against it with no re-preparation in between — an upgrade needing the suite's setup re-run is not a passing upgrade. Runs on release cut and on demand. Not on merge: it costs `matrix x workers` runners. On PRs, only when the diff touches the harness or these workflows. * E2E/Playwright: address review findings - Keep the to-phase loader read-only. It patched EmailSettings, FileSettings, SiteURL, AccessControlSettings and PluginSettings on the upgraded server before any assertion, so config the upgrade dropped was repaired instead of reported — the opposite of what this harness is for. - Return the raw post ID for a combined post. `<postId>:<timestamp>` has the same shape as a pending_post_id, so `getId` retried and then threw on posts that were already permanent. - Point `npm run clean` at `.mattermost_data`; it still removed the old `local_storage` name. - Trigger rolling upgrades on `package-lock.json` too, and document the full path set. - Quote the license placeholder in the local run instructions: bare `<key>` is a shell redirection, not a value. - Quote `$GITHUB_OUTPUT` in the two rolling-upgrade workflows. * E2E/Playwright: retarget the rolling-upgrade auto-trigger Every file under `lib/` matched, so any page-object edit fired the pipeline — `matrix size x workers` runners, each performing its own upgrade. Narrow the harness side to what actually defines an upgrade run: `upgrade-specs/`, the matrix resolver, and the rolling-upgrade workflows. Add the server surfaces an upgrade has to survive, which had no trigger at all: schema and config migrations, the app-layer data migrations, and the version list the from-version matrix is derived from. A new numbered migration is exactly what `upgrade-to`'s "schema migrations match baseline" check exists to catch. * E2E/Playwright: use repo-root paths in the trigger table `model/version.go` and `app/migrations.go` were ambiguous; spell them from the repo root so a contributor can match them against a diff. * E2E/Playwright: align rolling-upgrade trigger docs with the implemented matcher The trigger prose and table in e2e-tests-workflows.md still listed e2e-tests/playwright/lib/ and playwright.config.ts as auto-trigger paths, and marked merge-to-master/release-* as an auto-trigger. Neither matches e2e-tests-ci.yml's check-changes step: those two paths are unmatched and require the manual opt-in checkbox, and check-changes is never even called with run_rolling_upgrades=true on a merge (e2e-tests-on-merge.yml never passes it). Align both descriptions with the implemented matcher and with README.md's already-correct table. Co-authored-by: sabril <saturninoabril@users.noreply.github.com> * E2E/Playwright: fix write-after-end crash in collectLogs() teardown container.logs() follows a running container, so a stack left running (PW_TESTCONTAINERS_REUSE=true) never emits 'end' on its own. The 10s stall timeout then called outFile.end() while the source was still piping into it, so the next chunk landed on the closed destination and crashed the process with an unhandled ERR_STREAM_WRITE_AFTER_END. This intermittently killed rolling-upgrade worker jobs (seen failing the upgrade-from-release-11.10 lane on this PR). Unpipe and destroy the source before ending outFile, and add an error handler on outFile, so no write can land after it closes. Co-authored-by: sabril <saturninoabril@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: sabril <saturninoabril@users.noreply.github.com> |
||
|
|
5e6f509f36 |
Add deterministic i18n checkers for the server and webapp locale catalogs (#38231)
* Add deterministic i18n checkers for both surfaces
Nothing in CI reads the locale catalogs. webapp/channels has an
i18n-verify-translations script -- `formatjs verify --structural-equality` --
but no workflow calls it, and it has no extra-key check. The server has nothing
at all. So a translation that throws at format time, invents a variable the
source does not have, or carries an id en.json retired is invisible until a
user in that language hits it.
Webapp: scripts/check_icu.mjs, wired up as the existing i18n-verify-translations
script. It imports @formatjs/icu-messageformat-parser, declared as a dependency
of channels and pinned to 2.11.4 -- the copy react-intl 7.1.14 resolves -- so
"parses here" really does imply "parses there". The pin is load-bearing: three
versions coexist in the tree, and a bare import resolved to the 2.4.0 that
babel-plugin-formatjs hoists to the root, the build-time transformer's grammar,
seven minors behind the parser that actually renders the catalogs, and a dedupe
could have moved it again without notice. The header records that the pin has
to move with react-intl.
The checks are in two layers. Layer 1 is the runtime guarantee and always
fails: valid JSON, string values, key parity with en.json, every value parses,
and no variable or tag the source does not have (an unknown variable throws
MISSING_VALUE at format time; an unknown tag renders as raw markup). Layer 2 is
fidelity: a dropped variable or tag, a variable demoted to a different type --
{count, plural, ...} becoming a bare {count} still parses and renders, it just
silently stops pluralizing -- and an ASCII apostrophe before < or {, which in
ICU opens a quoted literal that swallows the tag and everything after it. That
last one still parses, so this check is the only thing that catches it.
Type parity compares the set of types each variable is used as rather than a
single type per variable. A name is routinely used in two roles at once --
"{count, number} new {count, plural, one {message} other {messages}}" -- and a
type-per-name map mis-flags every one of those.
The comparison is asymmetric, because only one direction is a defect. Demotion
loses the plural. Promotion is how a translator makes a sentence grammatical:
English needs a plural far less often than the languages it is translated into
-- "{count} items were deleted" is one sentence in English and four in Russian
-- and react-intl reads the value out of the same values object either way. A
PROMOTIONS table names the roles that are safe to widen into and everything
else stays fatal. The formatting types stay out of it: {x, number} over a
string renders NaN and {x, date} over a non-date renders "Invalid Date", so
reaching for one the source did not use is a defect rather than a grammar fix.
Server: `mmgotool i18n verify` and `make i18n-verify`. The strongest check
available there is the loader itself: go-i18n's LoadTranslationFile is what the
server runs at startup, so a file it rejects is a file that breaks the server.
Each locale loads into its own bundle rather than the package-global one, so
one file cannot mask a defect in another depending on the order they happen to
be walked in, and the checks can be run in isolation. On top of the loader, id
parity, {{.Field}} parity in both directions, and exactly the CLDR plural
categories the locale uses -- stricter than the webapp, because go-i18n rejects
a category the locale does not have.
Plural shape is compared against en.json in the collapsing direction too, since
flattening a pluralized entry into a single string still loads and still
renders and just silently stops pluralizing. The opposite direction is left
alone: whether a count reaches go-i18n is a property of the call site, not of
en.json -- the batched email title is a plain string in English and pluralized
in six locales, and email_batching.go passes len(notifications)-1 -- so a
language that has to inflect where English does not is translating correctly.
Empty plural forms are flagged as well. countEmptyItems and
removeEmptyTranslations both test the raw JSON for `""`, so every plural map
slips past them, and go-i18n builds no template for an empty form, finds none
at format time, and returns the translation id. Empty single-string
translations render that same raw id and are deliberately left alone: several
hundred already exist across es, fr and it, and `i18n clean-empty` already
removes exactly that shape.
There is no allowlist. A translation that deviates from its source is either
wrong, or the source is encoding English grammar the other languages cannot
follow -- and both of those are worth fixing rather than recording. Every key
that wanted an exemption while this was being written turned out to be the
second kind, and the four source strings responsible landed upstream in #38228,
so the corpus passes with nothing exempted. If a case ever turns up that
genuinely is a property of a target language, the mechanism is ten lines and
should be scoped to key and locale, which a real example would show how to do.
Both take a flag (--warn-missing-keys / --warn-missing-ids) that downgrades
missing entries to warnings, since key parity is a separate problem from
whether the entries that exist are correct.
Pulling in go-i18n resolves the minimum versions its go.mod requires --
pelletier/go-toml v1.2.0 and yaml.v2 v2.2.1, both with known advisories. Pinned
to the versions the server module already resolves, v1.9.5 and v2.4.0, so the
tool module and the server agree rather than each carrying its own copy.
The server tests cover each check the command makes: the runtime loader gate,
id parity in both directions and under --warn-missing-ids, template token
parity, plural shape against en.json in both directions, CLDR categories per
locale, and empty plural forms. They also pin the two behaviours the command
walk depends on, that en.json, non-JSON files and subdirectories are skipped.
Writing them turned up that the "no CLDR plural spec" branch is unreachable:
the loader derives the language with language.Parse, which only returns one
when GetPluralSpec is non-nil for the same tag, so a file that loaded always
has a spec. It is left in place as an invariant guard and labelled as one, with
the test asserting what an unrecognized filename actually does.
The webapp tests drive the real script in a subprocess, since its contract is
its exit code: 0 clean, 1 defects found, 2 misuse. They cover well-formedness,
key parity in both directions and under --warn-missing-keys, variable and tag
parity including names nested inside plural branches, the argument role rules
in both directions, and the apostrophe check with its doubled and typographic
escapes. The first test asserts that the resolved ICU parser is the version
react-intl depends on, which is the property the rest of the suite rests on and
the one that silently regressed before. It fails against a node_modules tree
installed before that pin landed; npm ci fixes it.
Both are wired into the i18n workflow, which until now only ever looked at
which files changed. The existing check-files job stays as it is -- reporting a
direct edit to a non-English catalog is still worth doing, and it answers a
different question from whether the edit is correct.
Both run with missing entries downgraded to warnings (--warn-missing-keys /
--warn-missing-ids). Key parity is a real gap, 55,164 entries on the webapp and
21,748 on the server, but it is a separate problem from whether the entries
that do exist are valid, and a hard parity gate is only fair once a contributor
adding a string has a documented way to produce the other 21 locales.
The workflow triggers on server/i18n/** and webapp/channels/src/i18n/**, and on
the checkers themselves -- check_icu.mjs, mmgotool, and the workflow files. It
is the only workflow that invokes either checker, so without those paths a PR
touching one of them never ran it and a broken checker could land green. That
matches how webapp-ci.yml already lists its own workflow and composite action.
The catalogs do not pass yet; the three commits that follow repair them.
* Fix the locale catalog defects the checkers found
Running the new checkers over the shipped catalogs turns up 2,474 webapp errors
and 1,495 server errors. They are not stylistic: 98 webapp strings fail to parse
with the parser react-intl runs, so they throw the moment a user in that
language opens the screen. vi has 36 of them, ro 18, fa 15. Another 395 name a
variable or tag the source does not have, which throws MISSING_VALUE or renders
raw markup, and on the server 57 interpolate a {{.Token}} that is not there and
render "<no value>".
Two kinds of change here, and no new translation of anything.
Repaired mechanically, where the intended text is unambiguous:
webapp 74 variable or tag the source renamed -- {fullUsers} -> {fullSeats},
{displayname} -> {displayName}, {linkApi} -> {linkAPI}. Paired
only when each unknown name has one clear counterpart.
17 an apostrophe opening an ICU quoted literal. After a letter it is
an elision, so it becomes the typographic apostrophe -- French
l'<link>; otherwise it is a quote mark and every lone apostrophe
in the string is doubled, which is the ICU escape and keeps an
opening and closing pair matched.
15 a translated variable name -- {потребителско име} for {username}.
These are MALFORMED_ARGUMENT, so the string never parsed at all.
3 a pseudo-tag the source quotes deliberately, '<blank>', copied
without the quoting and so an UNCLOSED_TAG.
server 22 a plural branch for a category the locale does not have. go-i18n
rejects those outright; pt-BR, es, fr and it carry a "many" that
its CLDR spec does not define.
14 a {{.Token}} the source renamed.
Removed, because the entry translates an English string that no longer exists:
webapp 1285 keys en.json no longer has
702 translations whose structure no longer matches the source
server 809 ids en.json no longer has
497 translations whose tokens or plural categories no longer match
Removing is the conservative option, not the destructive one. react-intl falls
back to the English defaultMessage for a key a catalog does not carry, and the
server falls back to the id; what these entries render today is a stray
'<your-mattermost-url>' literal, unrendered markdown, a missing hyperlink, or an
exception. The catalogs are already 55,164 webapp and 21,748 server entries
short of parity, so this does not change what a translator has to do next -- it
changes what a user sees in the meantime. Every removed entry is recoverable
from history if someone wants to repair rather than retranslate it.
Nothing here is exempted, because there is nothing to exempt with. Six keys
wanted an allowlist entry over the course of this work, and every one turned out
to be the same thing: an English sentence assembled from parts, forcing
translators into a shape the source did not have. The four source strings
responsible are fixed in the commit this branch is stacked on, which is why the
checker ships without an allowlist at all.
Both checkers now pass with --warn-missing-keys / --warn-missing-ids. Key parity
is a separate problem and is left as warnings.
* Fix the French batched email title plural forms
The entry used the CLDR category "many", which the go-i18n plural spec for
French does not define, and left "other" empty. French resolves any count of
two or more to "other", so every French batched email digest covering more
than one message shipped with an empty subject line.
Move the plural text into "other" and drop the unused category.
* Remove the half-translated plural entries from six locale catalogs
Eighteen entries across bg, es, fa, fr, it and ro filled "one" and left the
remaining categories empty. An empty form is worse than an absent translation:
go-i18n builds no template for it, finds none at format time, and returns the
translation id, so a Spanish user inviting two people who are already members
saw "api.command_invite.user_already_in_channel.app_error".
Deleting the entry lets the en fallback serve real English instead. No
translation is invented here -- the singular text goes away with the entry,
because a catalog that answers only for count == 1 cannot be served safely.
* Use singular agreement in the Italian and Romanian out-of-channel mention strings
|
||
|
|
53aaf6a4a8 |
Move changelog generation automation to server repo (#38113)
Automatic Merge |
||
|
|
cd9bcb005e |
chore(ci): warm Go module and build cache daily, restore read-only in CI (#38285)
* chore: extract install-<tool> Makefile targets
Pull each `go install` of a build tool into its own `install-<tool>`
target so the installs are declared once and reused as prerequisites,
and add `install-ci-tools` plus a `go-cache` target that pre-warms the
Go module and build caches.
Also collapse the duplicate `vet-api` target declarations into one.
* chore(ci): add restore-go-cache composite action
* chore(ci): mount warmed Go cache into run-in-buildenv
* chore(ci): add daily Go module/build cache warm workflow
* chore(ci): restore warmed Go cache read-only across server CI jobs
* Simplify Go cache key to glob-match all go.mod and go.sum files
* make GOBIN explicit
* Pass Go cache paths as step outputs, not GITHUB_ENV
restore-go-cache wrote GOMODCACHE/GOCACHE/GOBIN to GITHUB_ENV so later
steps would pick them up. zizmor flags any GITHUB_ENV write as a possible
code-execution vector, since an attacker-controlled value there can set
LD_PRELOAD or NODE_OPTIONS for every subsequent step.
Nothing untrusted reaches these particular values, but the write is also
unnecessary: expose them as action outputs and map them into the one step
that actually needs them, matching what
|
||
|
|
8b5045c27b | Ensure we only warm the node_modules cache from the master branch (#38342) | ||
|
|
791fc09b5d |
Test and lint mmgotool in CI (#38338)
* Fix the pre-existing lint failures in mmgotool Nothing lints this module yet, so these accumulated: an unchecked Close on the en.json write path, two unused walk parameters, a nil initialiser and three redundant breaks. The Close is the only behaviour change -- en.json is regenerated wholesale, so a failed flush would otherwise truncate it silently. * Test and lint mmgotool in CI tools-ci.yml only ever covered mattermost-govet, and each tool under tools/ is its own Go module that the server and mmctl suites cannot reach -- so mmgotool was tested and linted nowhere. Matrix both jobs over the two modules and give mmgotool the Makefile and golangci config the matrix expects. |
||
|
|
bbd208cffd |
chore(ci): bump pr-test-analysis toolkit ref to pick up claude-code-action v1.0.210 (#38335)
Points at mattermost/mattermost-test-automation-toolkit b51a481e3fe6332053be09800d59da1a8c3e3e2e, which upgrades anthropics/claude-code-action from v1.0.70 → v1.0.210. v1.0.210 includes anthropics/claude-code-action#1580 (shipped in v1.0.188), which passes no-cache: true to oven-sh/setup-bun. The upstream setup-bun key is not ref-aware, so every second-and-subsequent run against the same PR ref hit a 409 on save, and @actions/cache treated the HTML error body as transient and burned ~20-30s on 5 retries plus emitted a noisy warning per PR push. Disabling the cache is a net wallclock win — the 35 MB Bun binary downloads in 2-3s. Refs: - https://github.com/anthropics/claude-code-action/pull/1580 - https://github.com/anthropics/claude-code-action/issues/1252 - https://github.com/anthropics/claude-code-action/releases/tag/v1.0.210 |
||
|
|
1cf3c87173 |
Warm the Cypress deps cache from master and prefer it over the artifact (#38261)
* Warm the Cypress deps cache from master and prefer it over the artifact The parent commit hands the Cypress deps to the workers as a run-scoped artifact, which costs the shared Actions cache nothing but makes prep-deps install on every run. A baseline run measured the cache path it replaced at 6s per worker with prep-deps skipping the install entirely, so paying an install plus a ~275 MB upload on every run is a poor trade for the common case. Restore the fast path without restoring the churn. A new scheduled workflow becomes the sole writer of the deps key, warming it from master daily and on any push that changes the cypress lockfile. prep-deps then probes the key with lookup-only — a metadata call, not a download — and publishes the result as a job output. Workers restore the warmed entry on a hit and unpack the artifact only on a miss, so exactly one of the two paths runs. The net effect on the cache budget is one warm entry per master lockfile instead of one per lockfile any run happens to build; the four live entries today total 1.10 GB. PR and merge runs still never write. restore-cypress-deps centralises the key so the warm workflow, the probe and the workers cannot drift apart. Its payload must stay identical to pack/unpack-cypress-deps, since the cache entry and the artifact are interchangeable by construction. A separate binary-only cache is deliberately omitted: keyed on the same lockfile hash, it would invalidate in lockstep with the deps key and earn its keep only when one entry is evicted and the other survives. Keying it on the resolved cypress version instead would be a real win and can follow on its own. * Spell out which transport each Cypress deps action provides restore-cypress-deps and unpack-cypress-deps both leave a worker with node_modules and the Cypress binary, differing only in where they fetch them from, and nothing in their descriptions said so. Name the transport each one implements, when it is the one that runs, and the invariant that lets the workers accept either. No behaviour change; description metadata only. * Warm the cache only from master, even on manual dispatch * Report the Cypress deps cache outcome to the job summary Which transport a run took was only legible by opening prep-deps and reading which steps the probe skipped, and whether the warm workflow actually saved anything was just as buried. Report both to GITHUB_STEP_SUMMARY: prep-deps prints hit or miss, and the warm job prints saved or already warm. Both print the computed key alongside the verdict, which is what makes a miss diagnosable — a lockfile or path drift reads as an unexpected key rather than an unexplained miss. The workers are deliberately left alone. Their summary would be twenty near-identical lines per run, and the hit path already runs with fail-on-cache-miss, so a worker either restores or fails loudly. |
||
|
|
8f6d17d875 |
Tag the build server image on its Node version (#38281)
* Tag the build server image on its Node version
mattermost-build-server was published as :${GO_VERSION}, but the image
bakes NODE_VERSION into PATH at build time. Since setup-buildenv skips
the build whenever `docker manifest inspect` finds that tag, bumping the
Dockerfile's Node changed nothing: CI kept pulling the existing image,
and the new Node only appeared once a master merge overwrote the tag in
place -- which also silently changes what release branches pinned to
that tag resolve to.
Tag on both instead, e.g. go1.26.7-node24.11.1. A Node bump now names a
tag nobody has published, so it gets built and published exactly once,
the same way a Go bump does. The "go" prefix keeps these distinct from
the bare Go versions published under the old scheme.
server/build/buildenv-tag.sh is the one place that composes the tag, and
it verifies both Dockerfiles agree on Node since one tag covers both
images. CI resolves it where it already resolved the Go version and
propagates BUILD_SERVER_TAG, so the buildenv actions just take a string.
That leaves nothing needing a threaded Go version except the
actions/setup-go in server-test-template, which reads server/.go-version
via go-version-file. Only two of that template's seven callers shard at
all, so the rest were passing a value nothing read.
Node also moves to 24.11.1 to match .nvmrc, which is what the webapp's
engines constraint has expected for a while.
The buildenv job in server-ci.yml is renamed from "go" to match
server-ci-weekly and server-ci-nightly-race, so its check name changes
from "Compute Go Version" to "Build Environment".
* Fail the step when buildenv-tag.sh fails
`echo BUILD_SERVER_TAG=$(./buildenv-tag.sh)` reports echo's exit status,
not the script's, so under `bash -e` a failing script left the step green
and wrote an empty value:
$ bash -e -c 'echo "TAG=$(false)"; echo continued'
TAG=
continued
That defeated the consistency check the script exists for: on drift it
exits 1, the step passed anyway, and setup-buildenv went on to inspect
`mattermost-build-server:` with no tag -- failing well away from the
cause. Capture into a variable first, which `bash -e` does catch.
Applies to all nine call sites, including the ones already quoted;
quoting doesn't help here.
* Rebuild the buildenv image when a version pin changes
buildenv-tag.sh reads server/.go-version and .nvmrc, so name them in the
paths that trigger a republish.
Redundant in practice -- the script requires the pins and the Dockerfiles
to agree, so a valid pin change always carries a Dockerfile change, which
already triggers this -- but it makes the dependency explicit and fails
here rather than in Server CI.
* Pass buildenv inputs via env, not expression interpolation
GitHub expands `${{ }}` into the script before bash runs it, so
`echo "BUILDENV_TAG=${{ inputs.build-server-tag }}"` executed whatever
the input contained. That was reachable: buildenv-tag.sh reads
ARG NODE_VERSION straight out of a Dockerfile, and a fork PR controls
that file, so `ARG NODE_VERSION=$(...)` would have landed in the tag and
then in this script.
Pass all three inputs through env instead, and reject a tag that isn't
usable as a docker tag anyway.
Also fixes the aggregate's error message, which still named the 'go' job
after it was renamed to 'buildenv'.
* Don't write a literal expression in a shell comment
GitHub parses ${{ ... }} anywhere in the template, including inside a
run: block's comment text, so an empty one failed the action manifest:
"An expression was expected".
* Drop the redundant tag validation
The env mapping is the fix: bash gets the input as a value, never as
script text, so there is nothing left to inject with. The regex only
restated what docker would reject anyway.
* Validate the tag before emitting it
* Pass buildenv values as step outputs, not GITHUB_ENV
|
||
|
|
235bddbae0 |
Hand Cypress E2E deps to workers as a run-scoped artifact (#38253)
prep-deps wrote node_modules + the Cypress binary to a shared Actions cache keyed on the cypress lockfile, and the 40 workers read it back within the same run. The repo is pinned at the 10 GB Actions cache ceiling (100 entries, 9.99 GB), so LRU eviction runs continuously. Every distinct cypress lockfile a run happens to build costs a ~275 MB entry in that budget — four are live today, 1.10 GB total — and they evict entries other jobs depend on: the webapp node_modules cache (3.7 GB), the Playwright deps cache, and the E2E npm registry cache. Build the deps once in prep-deps and upload them as a run-scoped artifact instead. The handoff is unchanged in shape — one producer, N consumers, within a run — but it costs the shared cache nothing, and it drops the fail-on-cache-miss cliff the workers previously depended on. The artifact name is scoped by edition: e2e-tests-ci.yml invokes this template twice per workflow run (default and fips) and artifact names are run-scoped, so the two invocations would otherwise collide. prep-deps loses its cache-hit shortcut and now always installs, so it restores the master-warmed ~/.npm registry cache read-only to keep that install short, and its timeout goes 10 -> 15 minutes. Playwright still uses the cache handoff; it is a larger payload with a churnier key and follows separately. |
||
|
|
7b21e93e0d |
Allow direct modifications to non-English translation files (#38218)
i18n-ci-template.yml existed to fail any PR that touched a non-English translation file unless it came from the weblate account, back when translations arrived exclusively through Weblate. Translations can now be authored in-repo and land in the same PR as the English string they translate, so that gate rejects exactly the change it should be letting through. The changed-file detection stays -- a direct edit to a locale catalog is still worth surfacing on the PR -- but it emits a warning annotation instead of failing, and the weblate-account bypass is gone with nothing left to bypass. |
||
|
|
752e5d1755 |
MM-70307: Change Postgres test password to mostest_password (#38060)
* Change test password to mostest_password This makes the password compliant with the 112 bits minimum length requirement. Otherwise, FIPS-compliant OpenSSL implementations will panic when trying to connect from `lib/pq` with a shorter password. * Simplify test templates' POSTGRES_PASSWORD values * make generated * Modify missing "mostest" strings |
||
|
|
020e9dabdd |
ci: bump test-system-io-summary action for missed-spec status (#37804)
* ci: bump test-system-io-summary action for missed-spec status Placeholder bump pending merge of mattermost-test-system-io summary fix. Co-authored-by: saturnino <saturnino@mattermost.com> * ci: re-pin test-system-io-summary to main e2d5032 Replace the pre-merge placeholder SHA with the latest mattermost-test-system-io main commit, which includes the squash-merged missed-spec summary fix from #96. Co-authored-by: saturnino <saturnino@mattermost.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: saturnino <saturnino@mattermost.com> Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
ca6fd94e3d | chore: bump playwright workers to 20 (#38015) | ||
|
|
0912a75c9c | Bump minimum supported Postgres version to v15 (#37285) | ||
|
|
a70e8751d4 |
Gate ci-report and ci-artifacts on relevant-changed to fix skipped-job failures (#37777)
server-ci.yml always triggers now, but ci-report and ci-artifacts were missed when #37557 gated the other jobs on needs.go.outputs.relevant-changed. On PRs that don't touch server code, build-mattermost-server and the test jobs are skipped, so ci-artifacts fails trying to download a server-dist-artifact that was never uploaded, and ci-report fails trying to validate test logs that don't exist. Skip both cleanly instead. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
6b90bb3063 |
Make server-ci.yml always trigger so required checks never get stuck pending (#37557)
* Make server-ci.yml always trigger so required checks never get stuck pending Required status checks (e.g. "Server CI Complete") never get reported for a commit whose workflow run was skipped by the top-level `paths` filter, which leaves them stuck "Pending" forever and blocks merging PRs that don't touch server code. Move the path filtering into a job-level `if:` gated on a new `relevant-changed` output instead, so the workflow always runs and always reports a status. Co-authored-by: Cursor <cursoragent@cursor.com> * Fail ci-complete explicitly when the go job doesn't succeed Previously ci-complete ran unconditionally (if: always()) and its step just echoed a success message, so if the go job failed (e.g. the new relevant-changed detection step erroring out), all downstream jobs would be silently skipped due to unset outputs, yet ci-complete would still report "Server CI Complete" as green. Add go to needs and explicitly fail the step when needs.go.result != 'success', while keeping the existing always()/FIPS skip-tolerance behavior so the check still always gets reported. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
4cd57370ac | ci: remove testcontainers teardown step from playwright e2e workflow (#37697) | ||
|
|
a8c2307bee |
E2E/Playwright: Add testcontainers to playwright-lib (#37570)
* add testcontainers to playwright-lib * include retry mechanism for transient failures * - Introduced a new command `testcontainers:up` in package.json to run Playwright tests with Testcontainers. - Created a standalone Playwright configuration file `playwright.testcontainers-up.config.ts` for managing Testcontainers. - Added a no-op test `testcontainers_up.spec.ts` to ensure the Testcontainers stack is up during the test run. - Implemented a global setup script `testcontainers_up_global_setup.ts` to start and stop the Testcontainers stack. - Updated dependencies in package.json, including adding `chalk` for logging. * fix package-lock * fix tsc and restore waiting for all migrations to complete --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
c77efb5ab3 |
ci: update actions/test-system-io to latest with upload retries (#37612)
* ci: update actions/test-system-io to latest with upload retries * chore: update test system io actions --------- Co-authored-by: Mattermost Build <build@mattermost.com> |
||
|
|
8828e5a258 |
fix(docs-preview): set commit status via gh api for correct target_url (#37664)
* docs: mark scale partial pages as unlisted with proper titles These MDX files are content partials imported into the scale-to-*-users pages, mirroring the :orphan:/:nosearch: Sphinx pages they were migrated from. Without frontmatter, Docusaurus rendered them as full standalone pages with an ugly slug-derived title, breadcrumbs, and pagination. unlisted: true keeps their routes alive (needed by the PDF book builder and legacy redirects) while hiding them from the sidebar, breadcrumbs, pagination, and search indexing. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(docs-preview): set commit status via gh api for correct target_url The shared mattermost/actions/delivery/update-commit-status action accepts a target_url input but ignores it, hardcoding the Actions run URL instead. This made the docs-preview PR check's Details link point at the workflow run rather than the S3-hosted preview site. Switch docs-preview-template.yml and docs-preview-fork.yml to call the GitHub Statuses API directly via `gh api repos/.../statuses/{sha}`, matching the pattern in e2e-tests-playwright.yml, so the success status's target_url points at the actual preview URL. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
5fbb2a32d6 |
docs: vendor and stage Mattermost Agents docs for Docusaurus build (#37627)
* docs: vendor and stage Mattermost Agents docs for Docusaurus build The migration plan externalized Agents links, but the Docusaurus sidebar generator already declares an `agents` TOP_LEVEL category and stub pages link to `/agents/docs/*` paths with no content behind them. Vendor the mattermost-plugin-agents docs/ folder as a submodule and stage it into main/agents/docs/ at build time (mirroring how Sphinx handles the same submodule via conf.py excludes), so the Agents sidebar category and internal links resolve to real, versioned content instead of dead ends. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: strip leading license comment before MDX-staging Agents docs Three feature pages (channel_summaries.md, custom_prompts.md, managing_agents.md) lead with a raw <!-- Copyright ... --> HTML comment before the # Title line, which broke both title extraction (regex only matched a title at the very start of the file) and the Docusaurus build (MDX doesn't parse bare HTML comments the way Markdown does, so it failed to compile with "Unexpected character '!'"). Strip the license comment before further processing instead of trying to convert it. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: fail loudly on missing Agents submodule, fix usage comment path - A missing/uninitialized vendor/mattermost-plugin-agents submodule previously logged a warning and returned a successful zero-count result, letting CI/prestart/prebuild continue with an empty (or, on a reused workspace, stale) Agents section instead of failing. Now it clears any previously staged output first, then throws so the build fails clearly when the submodule isn't present. - Fixed the usage comment referencing the nonexistent docs-site/scripts path instead of the actual docs/site/scripts. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: checkout submodules in docs-ci.yaml build check This is the third docs-building workflow (alongside docs-cd.yml and docs-preview-template.yml) that runs npm run build on docs/site, and it was missed when submodules: true was added to the other two — its "Build docs site" step failed on this PR because vendor/mattermost-plugin-agents wasn't checked out, which stage-agents-docs.mjs now correctly treats as a hard failure. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: inline Agents content instead of a separate top-level LHS section Sphinx never gives Agents its own top-level nav entry: it `.. include::`s admin_guide.md/user_guide.md directly into the existing curated pages (administration-guide/configure/agents-admin-guide.rst and end-user-guide/agents.rst), and only nests a handful of files (providers, aws_bedrock_setup, sovereign_ai, usage_tips) as small hidden- toctree children of those same two pages. load-testing.md, upgrading_to_2.0.md, and all of features/*.md are never referenced in any Sphinx toctree at all. The previous vendoring approach staged all 12 files as a brand-new, disconnected top-level "Agents" category, so reaching the actual guide meant leaving the curated page, opening an unrelated top-level section, and drilling into "Docs" — the opposite of what Sphinx does today. This reproduces Sphinx's structure instead: - stage-agents-docs.mjs now categorizes the 12 vendored files: admin_guide/ user_guide are staged twice — once as a normal (but `unlisted: true`) doc for direct-link parity with Sphinx's orphan pages, and once as a Docusaurus Markdown partial (leading underscore, auto-excluded from routing/sidebars) for inline import. providers/aws_bedrock_setup/ sovereign_ai/usage_tips are staged as normal listed docs. Everything else is staged unlisted, matching Sphinx never surfacing them in nav. - Added generic relative-markdown-link rewriting (mirroring the existing image-path rewriting) so cross-links between vendored files (`../admin_guide.md#anchor`, `features/channel_summaries.md`, etc.) resolve correctly regardless of nesting depth or where the linking content ends up rendered. - agents-admin-guide.mdx and end-user-guide/agents.mdx now `import` and render the partials inline instead of linking out, and no longer link to a separate "Docs" section. - gen-documentation-sidebar.mjs: removed the standalone `agents` TOP_LEVEL category. Added `{doc: '<full id>'}` support to buildAdminConfigureItem for cross-directory group items, and a new ADMIN_CONFIGURE_GROUPS.agents group nesting providers/aws_bedrock_setup/ sovereign_ai under agents-admin-guide. Added a standalone promoteDocToCategory helper (End User Guide has no full manual-grouping override) to nest usage_tips under end-user-guide/agents. - Documented both patterns (cross-directory group items, Markdown-partial inlining) in docs/site/README.md's "Manual grouping overrides" section. Validated: stage-agents-docs.mjs + gen-documentation-sidebar.mjs both run clean with zero warnings; a scoped @mdx-js/mdx compile check across all 16 affected files (2 curated pages + 12 staged docs + 2 partials) passes with zero failures. Full `docusaurus build` still needs the OpenAPI prebuild's Go toolchain, unavailable in this sandbox — already covered by docs-ci.yaml in CI. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(agents): self-host the plugin-v2 upgrade guide link Now that mattermost-plugin-agents/docs is vendored and staged into /agents/docs/*, point the two "how to upgrade to Agents v2" references (v11 changelog, important upgrade notes) at our own /agents/docs/upgrading_to_2.0 page instead of an external GitHub blob link — the same self-hosting fix already applied to the old dangling sample-file links. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(agents): tighten stage-agents-docs.mjs header comment Same explanation, fewer words — trims the file-level comment from ~45 to ~33 lines without dropping the rationale (why staging instead of a direct submodule checkout, and how the three-way listed/unlisted/ inline-partial split reproduces Sphinx's nav). Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
5afa3b8cbf | ci: run yamllint on ubuntu-24.04 (#37641) | ||
|
|
24a5dd2859 |
ci: consolidate docs-preview checks into one status with preview URL (#37524)
* ci: consolidate docs-preview checks into one status with preview URL Drop the same-repo status-update jobs that cluttered the PR checks list, and post a single docs-preview commit status whose Details link opens the preview environment. Co-authored-by: Cursor <cursoragent@cursor.com> * remove comment * Remove duplicate failure status update in docs-preview-fork workflow The deploy job already posts a failure commit status via the shared template, making the separate update-failure-status job redundant. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
2d8a5f94ba |
Stop posting docs preview PR comments; link preview from status check (#37491)
Docs preview cleanup and build workflows no longer post PR comments. The build workflows now surface the preview URL via the commit status's target_url instead, so the status check links directly to the preview. Co-authored-by: Cursor <cursoragent@cursor.com> |