diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 1dd9853ea5..c47ed11a2a 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -38,9 +38,11 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} PR_AUTHOR: ${{ github.event.pull_request.user.login || github.event.issue.user.login }} BASE_ALLOWLIST: action@github.com,actions-user,ampagent,claude,comfy-pr-bot,GitHub Action,github-actions,github-actions[bot],Glary Bot,Glary-Bot,*[bot] + # For each commit emit the GitHub login when the author/committer email resolves to a GitHub account + # otherwise fall back to the raw git name. run: | others=$(gh api "repos/${{ github.repository }}/pulls/${PR_NUMBER}/commits" --paginate \ - --jq '.[] | (.author.login // empty), (.committer.login // empty)' \ + --jq '.[] | (.author.login // .commit.author.name // empty), (.committer.login // .commit.committer.name // empty)' \ | sort -u | grep -vix "${PR_AUTHOR}" | paste -sd, -) if [ -n "$others" ]; then echo "allowlist=${BASE_ALLOWLIST},${others}" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/pr-backport.yaml b/.github/workflows/pr-backport.yaml index 9d6ec183d7..5f8a4bdbe6 100644 --- a/.github/workflows/pr-backport.yaml +++ b/.github/workflows/pr-backport.yaml @@ -278,32 +278,49 @@ jobs: continue fi - # Create backport branch - git checkout -b "${BACKPORT_BRANCH}" "origin/${TARGET_BRANCH}" + # Create backport branch. A failure here (e.g. dirty state left + # by a prior target) must not abort the loop and skip remaining + # targets, so fall back to a clean checkout and record the error. + if ! git checkout -B "${BACKPORT_BRANCH}" "origin/${TARGET_BRANCH}"; then + echo "::error::Failed to create branch ${BACKPORT_BRANCH} for ${TARGET_BRANCH}" + FAILED="${FAILED}${TARGET_BRANCH}:branch-create-failed " + git checkout main || git checkout -f main + echo "::endgroup::" + continue + fi # Try cherry-pick if git cherry-pick "${MERGE_COMMIT}"; then if [ "$REMOTE_BACKPORT_EXISTS" = true ]; then - git push --force-with-lease origin "${BACKPORT_BRANCH}" + PUSH_CMD=(git push --force-with-lease origin "${BACKPORT_BRANCH}") else - git push origin "${BACKPORT_BRANCH}" + PUSH_CMD=(git push origin "${BACKPORT_BRANCH}") fi - echo "${BACKPORT_BRANCH}" >> "$CREATED_BRANCHES_FILE" - SUCCESS="${SUCCESS}${TARGET_BRANCH}:${BACKPORT_BRANCH} " - echo "Successfully created backport branch: ${BACKPORT_BRANCH}" + + # A push failure for one target must not abort the loop and + # prevent remaining targets from being attempted. + if "${PUSH_CMD[@]}"; then + echo "${BACKPORT_BRANCH}" >> "$CREATED_BRANCHES_FILE" + SUCCESS="${SUCCESS}${TARGET_BRANCH}:${BACKPORT_BRANCH} " + echo "Successfully created backport branch: ${BACKPORT_BRANCH}" + else + echo "::error::Failed to push ${BACKPORT_BRANCH} for ${TARGET_BRANCH}" + FAILED="${FAILED}${TARGET_BRANCH}:push-failed " + fi + # Return to main (keep the branch, we need it for PR) - git checkout main + git checkout main || git checkout -f main else # Get conflict info CONFLICTS=$(git diff --name-only --diff-filter=U | tr '\n' ',') - git cherry-pick --abort + git cherry-pick --abort || true echo "::error::Cherry-pick failed due to conflicts" FAILED="${FAILED}${TARGET_BRANCH}:conflicts:${CONFLICTS} " # Clean up the failed branch - git checkout main - git branch -D "${BACKPORT_BRANCH}" + git checkout main || git checkout -f main + git branch -D "${BACKPORT_BRANCH}" || true fi echo "::endgroup::" @@ -384,6 +401,10 @@ jobs: **Reason:** Merge conflicts detected during cherry-pick of `${MERGE_COMMIT_SHORT}` + The auto-backport could not be completed automatically. Please backport + manually onto branch `${BACKPORT_BRANCH}` (from `origin/${target}`) and + open a PR to `${target}`. +
📄 Conflicting files @@ -416,19 +437,37 @@ jobs: MERGE_COMMIT=$(jq -r '.pull_request.merge_commit_sha' "$GITHUB_EVENT_PATH") fi + # Post a comment without letting a single failed `gh pr comment` (e.g. + # a locked issue, as happened for PR #13359, or a transient API error) + # abort the step under `set -e` and swallow the remaining failures. + post_comment() { + local body="$1" + local context="$2" + if ! gh pr comment "${PR_NUMBER}" --body "${body}"; then + echo "::warning::Could not comment on PR #${PR_NUMBER} about ${context}. Manual backport required." + fi + } + for failure in ${{ steps.backport.outputs.failed }}; do IFS=':' read -r target reason conflicts <<< "${failure}" + SAFE_TARGET=$(echo "$target" | tr '/' '-') + BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}" + if [ "${reason}" = "branch-missing" ]; then - gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport failed: Branch \`${target}\` does not exist" + post_comment "@${PR_AUTHOR} Backport failed: Branch \`${target}\` does not exist" "missing branch ${target}" elif [ "${reason}" = "already-exists" ]; then - gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Commit \`${MERGE_COMMIT}\` already exists on branch \`${target}\`. No backport needed." + post_comment "@${PR_AUTHOR} Commit \`${MERGE_COMMIT}\` already exists on branch \`${target}\`. No backport needed." "already-backported ${target}" + + elif [ "${reason}" = "branch-create-failed" ]; then + gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport to \`${target}\` failed: could not create the backport branch. Please retry or backport manually." + + elif [ "${reason}" = "push-failed" ]; then + gh pr comment "${PR_NUMBER}" --body "@${PR_AUTHOR} Backport to \`${target}\` cherry-picked cleanly but the push failed. Please retry or push the backport branch manually." elif [ "${reason}" = "conflicts" ]; then CONFLICTS_INLINE=$(echo "${conflicts}" | tr ',' ' ') - SAFE_TARGET=$(echo "$target" | tr '/' '-') - BACKPORT_BRANCH="backport-${PR_NUMBER}-to-${SAFE_TARGET}" PR_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/${PR_NUMBER}" export PR_NUMBER PR_URL MERGE_COMMIT target BACKPORT_BRANCH CONFLICTS_INLINE @@ -444,10 +483,10 @@ jobs: CONFLICTS_BLOCK=$(echo "${conflicts}" | tr ',' '\n') MERGE_COMMIT_SHORT="${MERGE_COMMIT:0:7}" - export target MERGE_COMMIT_SHORT CONFLICTS_BLOCK AGENT_PROMPT PR_AUTHOR - COMMENT_BODY=$(envsubst '${target} ${MERGE_COMMIT_SHORT} ${CONFLICTS_BLOCK} ${AGENT_PROMPT} ${PR_AUTHOR}' <<<"$COMMENT_BODY_TEMPLATE") + export target MERGE_COMMIT_SHORT BACKPORT_BRANCH CONFLICTS_BLOCK AGENT_PROMPT PR_AUTHOR + COMMENT_BODY=$(envsubst '${target} ${MERGE_COMMIT_SHORT} ${BACKPORT_BRANCH} ${CONFLICTS_BLOCK} ${AGENT_PROMPT} ${PR_AUTHOR}' <<<"$COMMENT_BODY_TEMPLATE") - gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" + post_comment "${COMMENT_BODY}" "cherry-pick conflict on ${target} (backport manually onto ${BACKPORT_BRANCH})" fi done diff --git a/apps/website/e2e/navigation.spec.ts b/apps/website/e2e/navigation.spec.ts index 999cef164d..171bfb5e99 100644 --- a/apps/website/e2e/navigation.spec.ts +++ b/apps/website/e2e/navigation.spec.ts @@ -31,6 +31,26 @@ test.describe('Desktop navigation @smoke', () => { } }) + test('NEW badge shows on Products and Community only', async ({ page }) => { + const nav = page.getByRole('navigation', { name: 'Main navigation' }) + const desktopLinks = nav.getByTestId('desktop-nav-links') + + for (const label of ['Products', 'Community']) { + await expect( + desktopLinks + .getByRole('button', { name: label }) + .getByText('NEW', { exact: true }) + ).toBeVisible() + } + + await expect( + desktopLinks.getByRole('button', { name: 'Company' }).getByText('NEW') + ).toHaveCount(0) + await expect( + desktopLinks.getByRole('link', { name: 'Pricing' }).getByText('NEW') + ).toHaveCount(0) + }) + test('CTA buttons are visible', async ({ page }) => { const nav = page.getByRole('navigation', { name: 'Main navigation' }) const desktopCTA = nav.getByTestId('desktop-nav-cta') @@ -117,6 +137,27 @@ test.describe('Mobile menu @mobile', () => { } }) + test('NEW badge shows on Products and Community only', async ({ page }) => { + await page.getByRole('button', { name: 'Toggle menu' }).click() + + const menu = page.getByRole('dialog') + + for (const label of ['Products', 'Community']) { + await expect( + menu.getByRole('button', { name: label }).getByText('NEW', { + exact: true + }) + ).toBeVisible() + } + + await expect( + menu.getByRole('button', { name: 'Company' }).getByText('NEW') + ).toHaveCount(0) + await expect( + menu.getByRole('link', { name: 'Pricing' }).getByText('NEW') + ).toHaveCount(0) + }) + test('clicking section with subitems drills down and back works', async ({ page }) => { diff --git a/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-get-started-3-lg-visual-linux.png b/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-get-started-3-lg-visual-linux.png index 60fc0c7d1d..2abcd9e7e5 100644 Binary files a/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-get-started-3-lg-visual-linux.png and b/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-get-started-3-lg-visual-linux.png differ diff --git a/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-get-started-4-xl-visual-linux.png b/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-get-started-4-xl-visual-linux.png index cea1fb7d8d..65b5d114de 100644 Binary files a/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-get-started-4-xl-visual-linux.png and b/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-get-started-4-xl-visual-linux.png differ diff --git a/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-product-cards-3-lg-visual-linux.png b/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-product-cards-3-lg-visual-linux.png index a1f331a1fe..f96e4b9326 100644 Binary files a/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-product-cards-3-lg-visual-linux.png and b/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-product-cards-3-lg-visual-linux.png differ diff --git a/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-product-cards-4-xl-visual-linux.png b/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-product-cards-4-xl-visual-linux.png index f12988d9a8..80283bf8a8 100644 Binary files a/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-product-cards-4-xl-visual-linux.png and b/apps/website/e2e/visual-responsive.spec.ts-snapshots/home-product-cards-4-xl-visual-linux.png differ diff --git a/apps/website/src/components/common/HeaderMain/HeaderMainDesktop.vue b/apps/website/src/components/common/HeaderMain/HeaderMainDesktop.vue index c95737e084..7bd69fae31 100644 --- a/apps/website/src/components/common/HeaderMain/HeaderMainDesktop.vue +++ b/apps/website/src/components/common/HeaderMain/HeaderMainDesktop.vue @@ -16,6 +16,7 @@ import type { NavItem } from '../../../data/mainNavigation' import type { Locale } from '../../../i18n/translations' import NavColumn from './NavColumn.vue' import NavFeaturedCard from './NavFeaturedCard.vue' +import NewBadge from './NewBadge.vue' const { locale = 'en' } = defineProps<{ locale?: Locale }>() const mainNavigation = getMainNavigation(locale) @@ -42,7 +43,10 @@ function isNavItemActive(navItem: NavItem, path: string): boolean { - {{ navItem.label }} + + {{ navItem.label }} + +