Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/triage/comment.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Generate and return a GitHub comment following the template below.
```markdown
**[I was able to reproduce this issue. / I was unable to reproduce this issue.]** [1-2 sentences describing the result and key observations.]

**Fix:** [If `branchName` arg is non-null, include: [Create PR](https://github.com/withastro/astro/compare/{branchName}?expand=1)] **[I was able to fix this issue. / I was unable to fix this issue]** [1-2 sentences describing the solution and key observations. Even if no fix was created, you can still use this space to give guidance or "a best guess" at where the fix might be.]
**Fix:** **[I was able to fix this issue. / I was unable to fix this issue]** [1-2 sentences describing the solution and key observations. Even if no fix was created, you can still use this space to give guidance or "a best guess" at where the fix might be.] [If `branchName` arg is non-null, include this link: [View Fix](https://github.com/withastro/astro/compare/{branchName}?expand=1)]

**Cause:** [Single sentence explaining the root cause - or just the word "Unknown" if not determined.]

Expand Down
14 changes: 4 additions & 10 deletions .changeset/ripe-nights-feel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,10 @@
'astro': minor
---

Responsive images are now supported when `security.csp` is enabled, out of the box.
Adds support for responsive images when `security.csp` is enabled, out of the box.

Before, the styles for responsive images were injected using the `style="""` attribute, and the image would look like this:
Astro's implementation of responsive image styles has been updated to be compatible with a configured Content Security Policy.

```html
<img style="--fit: <value>; --pos: <value>" >
```
Instead of, injecting style elements at runtime, Astro will now generate your styles at build time using a combination of `class=""` and `data-*` attributes. This means that your processed styles are loaded and hashed out of the box by Astro.

After this change, styles now use a combination of `class=""` and data attributes. The image would look like this:

```html
<img class="__a_HaSh350" data-atro-fit="value" data-astro-pos="value" >
```
If you were previously choosing between Astro's CSP feature and including responsive images on your site, you may now use them together.
5 changes: 5 additions & 0 deletions .changeset/short-pears-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a case where setting `vite.server.allowedHosts: true` was turned into an invalid array
86 changes: 83 additions & 3 deletions .flue/workflows/issue-triage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ const fixResultSchema = v.object({
),
});

const labelResultSchema = v.object({
priority: v.pipe(
v.nullable(v.string()),
v.description(
'Exactly one priority label name (e.g. "P4: important"), or null if unable to determine priority',
),
),
packages: v.pipe(
v.array(v.string()),
v.description(
'One or more package label names (e.g. ["pkg: astro", "pkg: react"]). Empty array if unable to determine package.',
),
),
});

export default async function triage(flue: Flue) {
const { issueNumber } = flue.args as {
issueNumber: number;
Expand Down Expand Up @@ -135,16 +150,81 @@ Return only "yes" or "no" inside the ---RESULT_START--- / ---RESULT_END--- block
await flue.shell(`gh issue edit ${issueNumber} --remove-label "needs triage"`, {
env: { GH_TOKEN: flue.secrets.GITHUB_TOKEN },
});

// Fetch all repo labels and select appropriate priority + package labels.
const labelsJson = await flue.shell(
"gh api repos/withastro/astro/labels --paginate --jq '.[] | {name, description}'",
{ env: { GH_TOKEN: flue.secrets.GITHUB_TOKEN } },
);
const allLabels = labelsJson.stdout
.trim()
.split('\n')
.filter(Boolean)
.map((line) => JSON.parse(line) as { name: string; description: string });

// Filter to priority labels (P followed by a digit) and package labels (pkg: prefix)
const priorityLabels = allLabels.filter((l) => /^P\d/.test(l.name));
const packageLabels = allLabels.filter((l) => l.name.startsWith('pkg:'));
const candidateLabels = [...priorityLabels, ...packageLabels];

const labelResult = await flue.prompt(
`Label the following GitHub issue based on our Triage Report which summarizes what we learned in our attempt to reproduce, diagnose, and fix the issue.

Select the most appropriate labels from the list below. Use the label descriptions to guide your decision, combined with the triage report's cause and impact analysis.

### Rules
- Select exactly ONE priority label based on the severity and impact of the bug. Pay close attention to the "Cause" and "Impact" sections of the triage report.
- You must select ONE priorty label! If you are not sure, just use your best judgement based on the label descriptions and the findings of the triage report.
- Select 0-3 package labels based on where where the issue lives (or most likely lives) in the monorepo. The triage report's diagnosis should make it clear. If you cannot confidently determine the affected package(s), return an empty array for packages.
- Return the exact label names as they appear above — do not modify them.

### Priority Labels (select exactly one)
${priorityLabels.map((l) => `- **${l.name}**: ${l.description || '(no description)'}`).join('\n')}

### Package Labels (select zero or more)
${packageLabels.map((l) => `- **${l.name}**: ${l.description || '(no description)'}`).join('\n')}

---

<issue format="md">
# ${issue.title}

${issue.body}
</issue>

<triage-report format="md">
${comment}
</triage-report>
`,
{ result: labelResultSchema },
);

const labelsToAdd = [
...(labelResult.priority ? [labelResult.priority] : []),
...labelResult.packages,
].filter((name) => candidateLabels.some((l) => l.name === name));

if (labelsToAdd.length > 0) {
const labelFlags = labelsToAdd.map((l) => `--add-label ${JSON.stringify(l)}`).join(' ');
await flue.shell(`gh issue edit ${issueNumber} ${labelFlags}`, {
env: { GH_TOKEN: flue.secrets.GITHUB_TOKEN },
});
}
} else if (reproduceResult.skipped) {
// Triage was skipped due to a runner limitation. Keep "needs triage" so a
// maintainer can still pick it up, and add "auto triage skipped" to prevent
// the workflow from re-running on every new comment.
await flue.shell(`gh issue edit ${issueNumber} --add-label "auto triage skipped"`, {
env: { GH_TOKEN: flue.secrets.GITHUB_TOKEN },
});
} else {
// Not reproducible: add "needs repro" and remove "needs triage".
// We handle both labels here (instead of just adding "needs repro") to avoid
// the issue-labeled.yml workflow posting a duplicate auto-comment.
await flue.shell(
`gh issue edit ${issueNumber} --add-label "needs repro" --remove-label "needs triage"`,
{ env: { GH_TOKEN: flue.secrets.GITHUB_TOKEN } },
);
}

// If not reproducible: "needs triage" label stays.
// The loop continues when the author (or another user) replies.
return { reproduceResult, diagnoseResult, fixResult, isPushed };
}
17 changes: 10 additions & 7 deletions .github/workflows/issue-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ concurrency:

jobs:
triage:
# 1. Skip old issues (only run on issues created after this workflow was created)
# 2. Skip issues with "auto triage skipped" label
# 3. Skip pull requests (issues only)
# 4. One of these conditions must be met:
# 4a. Issue was just created
# 4b. Issue comment was just created, and "needs triage" label exists
# 1. Skip issues with "auto triage skipped" label
# 2. Skip pull requests (issues only)
# 3. One of these conditions must be met:
# 3a. Issue was just created
# 3b. Issue comment was just created, and "needs triage" label exists
if: >-
github.event.issue.created_at >= '2026-02-11' &&
!contains(github.event.issue.labels.*.name, 'auto triage skipped') &&
!github.event.issue.pull_request &&
((github.event.action == 'opened') ||
Expand All @@ -35,6 +33,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Configure Git identity
run: |
git config user.name "astrobot-houston"
git config user.email "fred+astrobot@astro.build"

- name: Setup PNPM
uses: pnpm/action-setup@v4

Expand Down
9 changes: 7 additions & 2 deletions packages/astro/src/core/config/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,13 @@ function mergeConfigRecursively(
}
}

// for server.allowedHosts, if the value is a boolean
if (key === 'allowedHosts' && rootPath === 'server' && typeof existing === 'boolean') {
// for server.allowedHosts, if either value is a boolean, don't merge as arrays
if (
key === 'allowedHosts' &&
rootPath === 'server' &&
(typeof existing === 'boolean' || typeof value === 'boolean')
) {
merged[key] = typeof value === 'boolean' ? value : existing;
continue;
}

Expand Down
20 changes: 20 additions & 0 deletions packages/astro/test/units/config/config-merge.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as assert from 'node:assert/strict';
import { describe, it } from 'node:test';
import { mergeConfig } from '../../../dist/core/config/index.js';

describe('mergeConfig', () => {
it('keeps server.allowedHosts as boolean', () => {
const defaults = {
server: {
allowedHosts: [],
},
};
const overrides = {
server: {
allowedHosts: true,
},
};
const merged = mergeConfig(defaults, overrides);
assert.equal(merged.server.allowedHosts, true);
});
});
Loading