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
137 changes: 137 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# 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.

# Path rules for the pull request labeler (.github/workflows/label_prs.yml).
# A label is added when a changed file matches one of its globs; labels are never removed,
# so hand-applied labels stay. area:ci and documentation apply only when every changed file
# matches, since most PRs touch a workflow or a doc in passing.

area:shuffle:
- changed-files:
- any-glob-to-any-file:
- 'native/shuffle/**'
- 'spark/src/main/**/shuffle/**'
- 'spark/src/main/scala/org/apache/comet/shuffle/**'
- 'common/src/main/**/*Shuffle*'

area:scan:
- changed-files:
- any-glob-to-any-file:
- 'native/core/src/parquet/**'
- 'native/core/src/cloud/**'
- 'native/core/src/execution/operators/scan.rs'
- 'native/core/src/execution/operators/csv_scan.rs'
- 'native/core/src/execution/operators/iceberg_scan.rs'
- 'native/core/src/execution/operators/iceberg_common.rs'
- 'spark/src/main/**/parquet/**'
- 'common/src/main/**/parquet/**'
- 'spark/src/main/scala/org/apache/comet/rules/CometScanRule.scala'
- 'spark/src/main/scala/org/apache/comet/rules/CometScanContrib.scala'
- 'spark/src/main/scala/org/apache/spark/sql/comet/*Scan*.scala'
- 'spark/src/main/scala/org/apache/comet/objectstore/**'
- 'spark/src/main/scala/org/apache/comet/serde/operator/*Scan*.scala'
- 'contrib/**'

area:writer:
- changed-files:
- any-glob-to-any-file:
- 'native/core/src/execution/operators/parquet_writer.rs'
- 'native/core/src/execution/operators/iceberg_write.rs'
- 'spark/src/main/scala/org/apache/spark/sql/comet/*Write*.scala'
- 'spark/src/main/scala/org/apache/spark/sql/comet/*Commit*.scala'
- 'spark/src/main/scala/org/apache/comet/serde/operator/*Writ*.scala'
- 'spark/src/main/scala/org/apache/comet/serde/operator/CometSink.scala'
- 'spark/src/main/scala/org/apache/comet/serde/operator/CometDataWritingCommand.scala'
- 'spark/src/main/**/*WriteFiles*'

area:aggregation:
- changed-files:
- any-glob-to-any-file:
- 'native/spark-expr/src/agg_funcs/**'
- 'spark/src/main/scala/org/apache/comet/serde/aggregates.scala'
- 'spark/src/main/scala/org/apache/comet/serde/CometAggregateExpressionSerde.scala'
- 'native/core/src/execution/merge_as_partial.rs'

area:expressions:
- changed-files:
- all-globs-to-any-file:
- 'native/spark-expr/src/**'
- '!native/spark-expr/src/agg_funcs/**'
- '!native/spark-expr/src/jvm_udf/**'
- changed-files:
- all-globs-to-any-file:
- 'spark/src/main/scala/org/apache/comet/serde/*.scala'
- '!spark/src/main/scala/org/apache/comet/serde/aggregates.scala'
- '!spark/src/main/scala/org/apache/comet/serde/CometAggregateExpressionSerde.scala'
- changed-files:
- any-glob-to-any-file:
- 'native/core/src/execution/expressions/**'
- 'spark/src/main/scala/org/apache/comet/expressions/**'
- 'spark/src/main/scala/org/apache/comet/codegen/**'

area:joins:
- changed-files:
- any-glob-to-any-file:
- 'native/core/src/execution/operators/dynamic_filter.rs'
- 'native/core/src/execution/operators/dynamic_filter/**'
- 'spark/src/main/scala/org/apache/comet/rules/RewriteJoin.scala'
- 'spark/src/main/scala/org/apache/comet/rules/CometPlanAdaptiveDynamicPruningFilters.scala'
- 'spark/src/main/**/*Join*'

area:ffi:
- changed-files:
- any-glob-to-any-file:
- 'native/jni-bridge/**'
- 'native/core/src/execution/columnar_to_row.rs'
- 'spark/src/main/**/vector/**'
- 'common/src/main/**/vector/**'
- 'spark/src/main/scala/org/apache/comet/NativeColumnarToRowConverter.scala'
- 'spark/src/main/scala/org/apache/spark/sql/comet/CometColumnarToRowExec.scala'
- 'spark/src/main/scala/org/apache/spark/sql/comet/CometNativeColumnarToRowExec.scala'
- 'spark/src/main/scala/org/apache/spark/sql/comet/CometSparkToColumnarExec.scala'

area:memory:
- changed-files:
- any-glob-to-any-file:
- 'native/core/src/execution/memory_pools/**'
- 'spark/src/main/**/*MemoryManager*'
- 'spark/src/main/**/*MemoryAllocator*'
- 'common/src/main/**/*MemoryAllocator*'
- 'common/src/main/**/*OutOfMemory*'

area:udf:
- changed-files:
- any-glob-to-any-file:
- 'native/spark-expr/src/jvm_udf/**'
- 'spark/src/main/scala/org/apache/comet/udf/**'
- 'spark/src/main/**/python/**'
- 'spark/src/main/**/*MapInBatch*'
- '.github/workflows/pyarrow_udf_test.yml'

area:Iceberg:
- changed-files:
- any-glob-to-any-file:
- '**/*[Ii]ceberg*'
- '**/*[Ii]ceberg*/**'

area:ci:
- changed-files:
- any-glob-to-all-files: '{.github/**,dev/ci/**,dev/release/**,dev/*.sh,dev/*.py,dev/*.xml,dev/*.toml}'

documentation:
- changed-files:
- any-glob-to-all-files: '{docs/**,**/*.md}'
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ jobs:
- name: Check micro benchmark runner
run: python3 dev/ci/check-benchmark-runner.py

- name: Check pull request type labeling
run: node --test dev/ci/pr-type-label.test.mjs

- name: Install actionlint
run: |
curl -sSfL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash | bash
Expand Down
87 changes: 87 additions & 0 deletions .github/workflows/label_prs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# 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: Label pull requests

# Runs on pull_request_target so it can label pull requests from forks. Nothing from the pull
# request is checked out or executed: the labeler reads the changed file list through the API
# and the title step reads the event payload.
on:
pull_request_target:
types: [opened, synchronize, reopened]

permissions:
contents: read
pull-requests: write

# One group per pull request with cancellation, so at most one run writes labels at a time:
# the labeler action reads the label set and writes it back whole, and two runs writing at
# once can drop a label the other just added. A cancelled or replaced run loses nothing
# because every step below is safe to repeat on the next event.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
label:
runs-on: ubuntu-slim
steps:
# Checks out the base branch, never the pull request's code: under pull_request_target the
# default ref is the base commit, and only dev/ci/pr-type-label.mjs is read from it.
- uses: actions/checkout@v7
with:
persist-credentials: false

# The type label follows the conventional commit prefix in the title, matching how
# maintainers label by hand. The decision reads the pull request's current labels, not the
# event payload, so a type label a maintainer applied before this run started stays and
# a run cut short by a newer push is completed by that push. Dependabot pull requests
# already carry their label.
- name: Label by title prefix
if: github.actor != 'dependabot[bot]'
uses: actions/github-script@v9
with:
script: |
const { typeLabelsToAdd } = await import(
`${process.env.GITHUB_WORKSPACE}/dev/ci/pr-type-label.mjs`
);
const { data: current } = await github.rest.issues.listLabelsOnIssue({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100,
});
const labels = typeLabelsToAdd(
context.payload.pull_request.title,
current.map((label) => label.name),
);
if (labels.length === 0) {
core.info('No type label to add');
return;
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels,
});

- name: Label by changed paths
uses: actions/labeler@v7
with:
configuration-path: .github/labeler.yml
sync-labels: false
53 changes: 53 additions & 0 deletions dev/ci/pr-type-label.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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.

// Type label for a pull request from its conventional commit prefix, mirroring how
// maintainers label by hand. Used by .github/workflows/label_prs.yml and tested by
// pr-type-label.test.mjs.

export const LABELS_BY_PREFIX = {
feat: ['enhancement'],
refactor: ['enhancement'],
chore: ['enhancement'],
fix: ['bug'],
perf: ['enhancement', 'performance'],
test: ['enhancement', 'test'],
doc: ['documentation'],
docs: ['documentation'],
ci: ['enhancement', 'build'],
build: ['enhancement', 'build'],
deps: ['dependencies'],
};

const TYPE_LABELS = new Set(Object.values(LABELS_BY_PREFIX).flat());

const PREFIX = /^(feat|fix|perf|test|docs?|chore|refactor|ci|build|deps)(\([^)]*\))?!?:/i;

// Labels to add for `title` given the pull request's current labels. Empty when the title has
// no conventional prefix or when any type label is already present, so a choice a maintainer
// has made stays as it is.
export function typeLabelsToAdd(title, currentLabels) {
const match = PREFIX.exec(title.trim());
if (!match) {
return [];
}
const present = new Set(currentLabels);
if ([...present].some((label) => TYPE_LABELS.has(label))) {
return [];
}
return LABELS_BY_PREFIX[match[1].toLowerCase()].filter((label) => !present.has(label));
}
44 changes: 44 additions & 0 deletions dev/ci/pr-type-label.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// 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.

import assert from 'node:assert/strict';
import { test } from 'node:test';
import { typeLabelsToAdd } from './pr-type-label.mjs';

test('adds the type label for a conventional title with no labels', () => {
assert.deepEqual(typeLabelsToAdd('fix: keep the entry alive', []), ['bug']);
assert.deepEqual(typeLabelsToAdd('perf(shuffle)!: reuse contexts', ['area:shuffle']), [
'enhancement',
'performance',
]);
assert.deepEqual(typeLabelsToAdd('chore(deps): bump actions', []), ['enhancement']);
});

test('leaves a type label a maintainer already applied, whatever the event carried', () => {
// The event payload showed no labels, but the pull request has one by the time the run starts.
assert.deepEqual(typeLabelsToAdd('fix: keep the entry alive', ['enhancement']), []);
assert.deepEqual(typeLabelsToAdd('perf: reuse contexts', ['performance']), []);
});

test('adds nothing for a title without a conventional prefix', () => {
assert.deepEqual(typeLabelsToAdd('Add Lance contrib build gate', []), []);
assert.deepEqual(typeLabelsToAdd('[draft] ci: pinned image', []), []);
});

test('does not repeat labels that are already present', () => {
assert.deepEqual(typeLabelsToAdd('test: cover struct columns', ['test']), []);
});
Loading