Skip to content

feat(connectors): add Apache Fluss sink connector #179

feat(connectors): add Apache Fluss sink connector

feat(connectors): add Apache Fluss sink connector #179

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
name: Issue Labeler / Assigner
# Three add-only triage steps that run when a reporter opens or edits an issue:
#
# 1. Label: maps the "Affected area / component" dropdown from the bug/feature
# issue forms (.github/ISSUE_TEMPLATE/) onto real repo labels, so reporters
# who lack triage permission still get their issue categorized at creation.
#
# 2. Assign: if the reporter ticked the "Contribution" checkbox ("I'm willing
# to submit a pull request ..."), assign the issue to its author so the
# volunteer is on record. addAssignees is idempotent and issue authors are
# always assignable, even as outside contributors.
#
# 3. Good first issue: if the reporter ticked the "Good first issue" checkbox,
# self-tag the issue with the `good first issue` label. Maintainers remove it
# during triage if they disagree (add-only here, see below).
#
# Add-only by design: no step removes anything. Reporters rarely deselect, and
# removing would fight maintainers who labeled or reassigned by hand. Every API
# call is idempotent, so re-running on `edited` is harmless.
#
# SECURITY: the issue body is attacker-controlled text. The label step parses
# it only for exact matches against the fixed AREA_LABELS allowlist below. The
# assign and good-first-issue steps use the body only as a boolean gate; the
# assignee comes from the trusted `payload.issue.user.login` and the label is a
# fixed constant, never body-derived, so the body cannot inject either. No
# checkout, no exec of issue contents, no token export.
on:
issues:
types: [opened, edited]
permissions:
issues: write
contents: read
jobs:
triage:
runs-on: ubuntu-24.04-arm
timeout-minutes: 5
steps:
- name: Apply area labels from issue form
uses: actions/github-script@v9
with:
script: |
// Dropdown option text -> repo label. Keep in sync with the
// `Affected area / component` options in
// .github/ISSUE_TEMPLATE/{bug_report,feature_request}.yml.
const AREA_LABELS = {
'Iggy server': 'server',
'Rust SDK': 'rust',
'Go SDK': 'go',
'Java SDK': 'java',
'Python SDK': 'python',
'C# SDK': 'csharp',
'Node.js SDK': 'javascript',
'C++ SDK': 'cpp',
'PHP SDK': 'php',
'CLI': 'tui',
'Web UI': 'web',
'Connectors': 'connectors',
'MCP server': 'mcp',
'Configuration': 'config',
'Wire protocol / API': 'api',
'Metadata': 'metadata',
'Clustering / replication': 'cluster',
'Performance': 'performance',
'Documentation': 'docs',
'CI / build / tooling': 'CI/CD',
// 'Other / not sure' intentionally maps to no label.
};
const HEADING = 'Affected area / component';
const body = context.payload.issue.body || '';
// Issue-form bodies render as `### <label>\n\n<value>`; capture
// the dropdown's value block, up to the next `###` or the end.
const esc = HEADING.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const section = body.match(
new RegExp('###[ \\t]+' + esc + '[ \\t]*\\n+([\\s\\S]*?)(?:\\n###|$)'),
);
// Multi-select renders the picks comma-separated on one line.
// Option text never contains a comma, so split on it safely.
const picks = (section ? section[1] : '')
.split(',')
.map(s => s.trim())
.filter(Boolean);
const labels = [...new Set(
picks.map(p => AREA_LABELS[p]).filter(Boolean),
)];
if (labels.length === 0) {
core.info('no mappable area selected, nothing to label');
return;
}
core.info(`applying labels: ${labels.join(', ')}`);
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
labels,
});
- name: Assign author who volunteered to contribute
uses: actions/github-script@v9
with:
script: |
// The "Contribution" checkbox in both issue forms renders as a
// task-list line. Checked, it reads `- [x] I'm willing to submit a
// pull request ...`; the tail differs per template (bug: "to fix
// this bug", feature: "to implement this feature"), so match the
// shared prefix only.
const body = context.payload.issue.body || '';
const volunteered =
/(^|\n)\s*-\s*\[[xX]\]\s+I'm willing to submit a pull request/.test(body);
if (!volunteered) {
core.info('author did not volunteer to contribute, nothing to assign');
return;
}
// Assignee is the trusted author from the event payload, never the
// body. addAssignees is a no-op if already assigned and silently
// ignores non-assignable users; issue authors are always assignable.
const author = context.payload.issue.user.login;
core.info(`assigning volunteering author: ${author}`);
await github.rest.issues.addAssignees({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
assignees: [author],
});
- name: Self-tag good first issue
uses: actions/github-script@v9
with:
script: |
// The "Good first issue" checkbox in both issue forms renders as a
// task-list line. Checked, it reads `- [x] I think this could be a
// good first issue ...`. Reporters self-tag; maintainers may remove
// the label during triage if they disagree. addLabels is idempotent
// and add-only, consistent with the other triage steps here.
const body = context.payload.issue.body || '';
const suggested =
/(^|\n)\s*-\s*\[[xX]\]\s+I think this could be a good first issue/.test(body);
if (!suggested) {
core.info('reporter did not suggest good first issue, nothing to label');
return;
}
core.info('applying label: good first issue');
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
labels: ['good first issue'],
});