Skip to content
86 changes: 86 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Bug report
description: Report something that is broken or not behaving as expected.
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for filing a bug. Fields marked required drive our triage automation
(scope labeling and the completeness check). Please fill in as much as you
can — a bot will tag you if anything required is missing.

- type: dropdown
id: scope
attributes:
label: Scope / Area
Comment thread
mhaack marked this conversation as resolved.
description: Which part of the product does this touch?
options:
- Editor / Canvas
- Blocks / Rendering
- Config / Sheets
- Auth / Permissions
- AI Assistant / Agent
- MCP / Tooling
- Infra / CI
- Docs
- Other / Unsure
validations:
required: true

- type: dropdown
id: affected
attributes:
label: Who is affected?
description: Is this customer-facing or internal?
options:
- Customer-facing
- Internal
validations:
required: true

- type: textarea
id: what-happened
attributes:
label: What happened?
description: A clear description of the bug, including what you expected instead.
validations:
required: true

- type: textarea
id: repro-steps
attributes:
label: Steps to reproduce
description: Minimal, numbered steps someone else can follow.
placeholder: |
1. Go to ...
2. Click ...
3. See error
validations:
required: true

- type: textarea
id: repro-links
attributes:
label: Reproduction link(s)
description: URL(s) where the bug can be observed (before/after, test site, deployed branch).
placeholder: |
- Before: https://main--{repo}--{owner}.aem.live/
- After: https://<branch>--{repo}--{owner}.aem.live/
Comment thread
mhaack marked this conversation as resolved.
validations:
required: true

- type: input
id: video
attributes:
label: Video (optional)
description: A short screen recording helps a lot, but it is not required.
validations:
required: false
Comment thread
anfibiacreativa marked this conversation as resolved.

- type: input
id: version
attributes:
label: Environment / version
description: Branch, commit, or deployed URL where you saw this.
validations:
required: false
Comment thread
anfibiacreativa marked this conversation as resolved.
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Question or discussion
url: https://github.com/adobe/da-nx/discussions
about: For open-ended questions and ideas, please start a discussion instead of an issue.
55 changes: 55 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Comment thread
anfibiacreativa marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Feature request
description: Suggest a new capability or an improvement.
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for the idea. Fields marked required drive our triage automation
(scope labeling and the completeness check). A bot will tag you if
anything required is missing.

- type: dropdown
id: scope
attributes:
label: Scope / Area
description: Which part of the product does this touch?
options:
- Editor / Canvas
- Blocks / Rendering
- Config / Sheets
- Auth / Permissions
- AI Assistant / Agent
- MCP / Tooling
- Infra / CI
- Docs
- Other / Unsure
validations:
required: true

- type: dropdown
id: affected
attributes:
label: Who is affected?
description: Is this customer-facing or internal?
options:
- Customer-facing
- Internal
validations:
required: true

- type: textarea
id: problem
attributes:
label: Problem / motivation
description: What are you trying to do, and why is it hard today?
validations:
required: true

- type: textarea
id: proposal
attributes:
label: Proposed solution
description: What would you like to see happen?
validations:
required: false
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Fix #<gh-issue-id>
Test URLs:
- Before: https://main--{repo}--{owner}.hlx.live/
- After: https://<branch>--{repo}--{owner}.hlx.live/

99 changes: 99 additions & 0 deletions .github/workflows/issue-completeness.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Issue completeness check

# Verifies a new/edited issue has the information triage needs. If anything is
# missing it applies the `triage-incomplete` label and leaves a single comment
# (tagging the reporter) listing what's needed. The comment is updated in place
# as the issue is fixed, and both the comment and label clear once it's complete.
#
# Required: a description, customer-vs-developer, a priority (P0-P3), and — for
# bugs — reproduction steps and a reproduction link. A video is prompted but
# never blocks.

on:
issues:
types: [opened, edited]

permissions:
issues: write

jobs:
check:
# Don't react to issues opened by bots (avoids automation loops).
if: ${{ github.event.issue.user.type != 'Bot' }}
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
const MARKER = '<!-- issue-completeness-bot -->';
const INCOMPLETE = 'triage-incomplete';

const body = context.payload.issue.body || '';
const author = context.payload.issue.user.login;
const { owner, repo } = context.repo;
const issue_number = context.payload.issue.number;
const labels = (context.payload.issue.labels || []).map(l => l.name);
const isBug = labels.includes('bug');

const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// Grab the first non-empty line under a "### <label>" heading.
const val = (label) => {
const m = body.match(new RegExp('###\\s*' + esc(label) + '\\s*\\n+([^\\n]+)'));
return m ? m[1].trim() : '';
};
const isEmpty = (v) => !v || /^_no response_$/i.test(v.trim());

const missing = [];
const description = val('What happened?') || val('Problem / motivation');
if (isEmpty(description)) missing.push('A description of the issue');
if (isEmpty(val('Who is affected?')))
missing.push('Who is affected (**customer-facing** or **internal**)');
if (isBug) {
if (isEmpty(val('Steps to reproduce'))) missing.push('**Steps to reproduce**');
if (isEmpty(val('Reproduction link(s)'))) missing.push('A **reproduction link**');
}
const videoMissing = isBug && isEmpty(val('Video (optional)'));

// Find our existing tracked comment, if any.
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number, per_page: 100,
});
const existing = comments.find(c => (c.body || '').includes(MARKER));

const hasLabel = labels.includes(INCOMPLETE);
const upsert = async (bodyMd) => {
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body: bodyMd });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number, body: bodyMd });
}
};

if (missing.length) {
if (!hasLabel) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [INCOMPLETE] });
}
let md = `${MARKER}\nHi @${author} :wave: thanks for opening this! `
+ `Before we can triage it, please add the following:\n\n`
+ missing.map(m => `- [ ] ${m}`).join('\n') + '\n';
if (videoMissing) {
md += `\n_Optional:_ a short screen recording of the problem helps a lot, but it is not required.\n`;
}
md += `\nEdit the issue description to add these — this notice and the \`${INCOMPLETE}\` `
+ `label clear automatically once everything is filled in.`;
await upsert(md);
core.info('Marked incomplete. Missing: ' + missing.join('; '));
} else {
if (hasLabel) {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: INCOMPLETE }).catch(() => {});
}
if (existing) {
const done = `${MARKER}\nThanks @${author} — everything triage needs is here. :white_check_mark:`
+ (videoMissing ? ` (A short screen recording would still help if you can add one.)` : '');
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body: done });
} else if (videoMissing) {
await upsert(`${MARKER}\nThanks @${author} — this looks complete. If you can, a short screen `
+ `recording would help us reproduce faster. Not required, just helpful!`);
}
core.info('Issue is complete.');
}
71 changes: 71 additions & 0 deletions .github/workflows/slack-pr-ticker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Slack PR Ticker

# Posts a one-line notice to Slack when a PR is opened / marked ready / reopened.
# Core PRs go to one channel, external PRs to another. Core vs external is decided
# from an explicit allowlist of core author usernames (CORE_AUTHORS below).
#
# Uses pull_request_target so the org secrets (webhook URLs) are available even
# for PRs from forks. This job only reads event metadata and never checks out or
# runs code from the PR, so it is not exposed to the pull_request_target risk of
# executing untrusted code.
#
# Required secrets:
# SLACK_WEBHOOK_CORE_PRS Incoming Webhook URL for the core-team channel
# SLACK_WEBHOOK_COMMUNITY_PRS Incoming Webhook URL for the contributor channel

on:
pull_request_target:
types: [opened, ready_for_review, reopened]

permissions:
contents: read

jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Route and post to Slack
env:
CORE_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_CORE_PRS }}
COMMUNITY_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_COMMUNITY_PRS }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
PR_DRAFT: ${{ github.event.pull_request.draft }}
REPO: ${{ github.repository }}
# Core contributors (edit to keep in sync with the team).
CORE_AUTHORS: 'svynod hannesolo mhaack anfibiacreativa'
run: |
set -euo pipefail

# Don't ticker drafts; the ready_for_review event covers them later.
if [ "$PR_DRAFT" = "true" ]; then
echo "Draft PR; skipping."
exit 0
fi

WEBHOOK="$COMMUNITY_WEBHOOK"; TIER="External Contribution"
for u in $CORE_AUTHORS; do
if [ "$(echo "$u" | tr '[:upper:]' '[:lower:]')" = "$(echo "$PR_AUTHOR" | tr '[:upper:]' '[:lower:]')" ]; then
WEBHOOK="$CORE_WEBHOOK"; TIER="Core Contribution"; break
fi
done

if [ -z "${WEBHOOK:-}" ]; then
echo "No webhook configured for tier=$TIER; skipping."
exit 0
fi

# Send discrete fields so a Slack Workflow Builder webhook can map them
# to variables (repo, number, title, author, url, tier) and compose the
# message itself. Raw URLs auto-linkify in Slack.
PAYLOAD="$(jq -n \
--arg repo "$REPO" \
--arg number "$PR_NUMBER" \
--arg title "$PR_TITLE" \
--arg author "$PR_AUTHOR" \
--arg url "$PR_URL" \
--arg tier "$TIER" \
'{repo: $repo, number: $number, title: $title, author: $author, url: $url, tier: $tier}')"
curl -sS -X POST -H 'Content-type: application/json' --data "$PAYLOAD" "$WEBHOOK"
Loading
Loading