This is an automated email from the ASF dual-hosted git repository.

Yicong-Huang pushed a commit to branch release/v1.1.0-incubating
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/release/v1.1.0-incubating by 
this push:
     new f3c535280d ci: add a strict license-binary checker that files a 
tracking issue on license drift (#4734)
f3c535280d is described below

commit f3c535280dd8b662d241f6b2000200e74ddba563
Author: Jiadong Bai <[email protected]>
AuthorDate: Sun May 3 09:19:10 2026 +0000

    ci: add a strict license-binary checker that files a tracking issue on 
license drift (#4734)
    
    ### What changes were proposed in this PR?
    
    This PR:
    
    - add a new parameter to `build.yml` to tune the mode of the license
    checking
    - add a new CI: `license-binary-nightly.yml` that include one `build`
    job that does `uses: ./.github/workflows/build.yml` with `mode` to be
    `release`, and one `report` job that will raise an issue if there is a
    drift on the license binary.
    
    ### Any related issues, documentation, discussions?
    
    Resolves #4692.
    
    ### How was this PR tested?
    
    Verified using my personal fork:
    
    https://github.com/bobbai00/texera/issues/6
    https://github.com/bobbai00/texera/actions/runs/25273894144
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (claude-opus-4-7)
    
    ---------
    
    (backported from commit 7dc68ba6425ce8cca872cb49ad5f561efd95ee1f)
    
    Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
 .github/workflows/build.yml                  |  16 ++-
 .github/workflows/license-binary-checker.yml | 176 +++++++++++++++++++++++++++
 2 files changed, 187 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 94472a0a35..a0874238f4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -56,6 +56,12 @@ on:
         required: false
         type: boolean
         default: true
+      mode:
+        # PR (default) | nightly | release. Only "PR" passes
+        # --ignore-transitive-version to check_binary_deps.py.
+        required: false
+        type: string
+        default: PR
 
 env:
   NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
@@ -113,7 +119,7 @@ jobs:
         run: yarn --cwd frontend run build:ci
       - name: Check bundled npm packages against per-module LICENSE-binary 
files
         if: matrix.os == 'ubuntu-latest'
-        run: ./bin/licensing/check_binary_deps.py --ignore-transitive-version 
npm frontend/dist/3rdpartylicenses.json
+        run: ./bin/licensing/check_binary_deps.py ${{ inputs.mode == 'PR' && 
'--ignore-transitive-version' || '' }} npm frontend/dist/3rdpartylicenses.json
       - name: Run frontend unit tests
         run: yarn --cwd frontend run test:ci --code-coverage
       - name: Upload frontend coverage to Codecov
@@ -209,7 +215,7 @@ jobs:
           unzip -q amber/target/universal/amber-*.zip -d /tmp/dists/
 
           check_exit=0
-          ./bin/licensing/check_binary_deps.py --ignore-transitive-version jar 
\
+          ./bin/licensing/check_binary_deps.py ${{ inputs.mode == 'PR' && 
'--ignore-transitive-version' || '' }} jar \
             --license-binary amber/LICENSE-binary-java \
             /tmp/dists/amber-*/lib || check_exit=$?
           ./bin/licensing/audit_jar_licenses.py /tmp/dists/amber-*/lib || true
@@ -330,7 +336,7 @@ jobs:
           unzip -q ${{ matrix.service }}/target/universal/${{ matrix.service 
}}-*.zip -d /tmp/dists/
 
           check_exit=0
-          ./bin/licensing/check_binary_deps.py jar \
+          ./bin/licensing/check_binary_deps.py ${{ inputs.mode == 'PR' && 
'--ignore-transitive-version' || '' }} jar \
             --license-binary ${{ matrix.service }}/LICENSE-binary \
             /tmp/dists/${{ matrix.service }}-*/lib || check_exit=$?
           ./bin/licensing/audit_jar_licenses.py /tmp/dists/${{ matrix.service 
}}-*/lib || true
@@ -395,7 +401,7 @@ jobs:
         run: pip-licenses --format=csv --ignore-packages pip-licenses 
prettytable wcwidth > /tmp/pip-licenses.csv
       - name: Check installed Python packages against per-module 
LICENSE-binary files
         if: matrix.python-version == '3.12'
-        run: ./bin/licensing/check_binary_deps.py --ignore-transitive-version 
python /tmp/pip-licenses.csv
+        run: ./bin/licensing/check_binary_deps.py ${{ inputs.mode == 'PR' && 
'--ignore-transitive-version' || '' }} python /tmp/pip-licenses.csv
       - name: Create iceberg catalog database
         run: psql -h localhost -U postgres -f sql/iceberg_postgres_catalog.sql
         env:
@@ -456,7 +462,7 @@ jobs:
           bun run bin/collect-licenses.ts > dist/3rdpartylicenses.json
       - name: Check bundled agent-service packages against per-module 
LICENSE-binary files
         if: matrix.os == 'ubuntu-latest'
-        run: ../bin/licensing/check_binary_deps.py --ignore-transitive-version 
agent-npm dist/3rdpartylicenses.json
+        run: ../bin/licensing/check_binary_deps.py ${{ inputs.mode == 'PR' && 
'--ignore-transitive-version' || '' }} agent-npm dist/3rdpartylicenses.json
       - name: Install development dependencies
         run: bun install --frozen-lockfile
       - name: Lint with Prettier
diff --git a/.github/workflows/license-binary-checker.yml 
b/.github/workflows/license-binary-checker.yml
new file mode 100644
index 0000000000..069d99aa60
--- /dev/null
+++ b/.github/workflows/license-binary-checker.yml
@@ -0,0 +1,176 @@
+# 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: License Binary Checker
+
+# Scheduled drift checker for per-module LICENSE-binary files. Calls
+# build.yml with mode=nightly (strict) and files / updates / closes
+# one tracking issue (label `license-binary-drift`). Sub-task of #4688.
+on:
+  schedule:
+    # 11:00 UTC daily = 04:00 PDT / 03:00 PST.
+    - cron: "0 11 * * *"
+  workflow_dispatch:
+
+permissions:
+  contents: read
+  issues: write          # create / update / close the tracking issue
+  actions: read          # listJobsForWorkflowRun in the report step
+
+concurrency:
+  group: license-binary-checker
+  cancel-in-progress: false
+
+jobs:
+  build:
+    uses: ./.github/workflows/build.yml
+    with:
+      mode: nightly
+    secrets: inherit
+
+  report:
+    if: always()
+    needs: [build]
+    runs-on: ubuntu-latest
+    steps:
+      - name: File or update tracking issue
+        uses: actions/github-script@v8
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
+          script: |
+            const TRACKING_LABEL = 'license-binary-drift';
+            const TITLE = 'License-binary drift detected';
+            // Match license-check step names; ignore unrelated failures.
+            const LICENSE_STEP_RE = /license[-\s]?binary|binary licenses/i;
+
+            const { owner, repo } = context.repo;
+            const runUrl =
+              
`${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}`;
+
+            // Walk every job in the current run (which includes the called
+            // build.yml jobs, prefixed with the calling job id `build`).
+            const jobs = await github.paginate(
+              github.rest.actions.listJobsForWorkflowRun,
+              { owner, repo, run_id: context.runId, per_page: 100 }
+            );
+
+            const licenseFailures = [];
+            const licenseSuccesses = [];
+            for (const job of jobs) {
+              for (const step of job.steps || []) {
+                if (!LICENSE_STEP_RE.test(step.name)) continue;
+                if (step.conclusion === 'failure') {
+                  licenseFailures.push({
+                    job: job.name,
+                    step: step.name,
+                    url: job.html_url,
+                  });
+                } else if (step.conclusion === 'success') {
+                  licenseSuccesses.push({ job: job.name, step: step.name });
+                }
+              }
+            }
+
+            const anyDrift = licenseFailures.length > 0;
+            core.info(
+              `License-check steps: ${licenseSuccesses.length} ok, ` +
+              `${licenseFailures.length} failed.`
+            );
+
+            const { data: existing } = await github.rest.issues.listForRepo({
+              owner, repo, state: 'open', labels: TRACKING_LABEL, per_page: 5,
+            });
+
+            // Only manage issues for runs against the default branch.
+            const defaultBranch = context.payload.repository.default_branch;
+            const isDefaultBranch =
+              context.ref === `refs/heads/${defaultBranch}` ||
+              context.eventName === 'schedule';
+            if (!isDefaultBranch) {
+              core.info(
+                `Not on default branch (ref=${context.ref}); ` +
+                `skipping issue management. Drift detected: ${anyDrift}.`
+              );
+              return;
+            }
+
+            if (!anyDrift) {
+              if (existing.length === 0) {
+                core.info('No drift, no existing tracking issue. Nothing to 
do.');
+                return;
+              }
+              for (const issue of existing) {
+                await github.rest.issues.update({
+                  owner, repo, issue_number: issue.number,
+                  state: 'closed', state_reason: 'completed',
+                });
+                await github.rest.issues.createComment({
+                  owner, repo, issue_number: issue.number,
+                  body:
+                    `Closed automatically: scheduled run ${runUrl} found ` +
+                    `no license-binary drift across any ecosystem.`,
+                });
+                core.info(`Closed stale tracking issue #${issue.number}`);
+              }
+              return;
+            }
+
+            // Body shape mirrors ISSUE_TEMPLATE/task-template.yaml.
+            const failureLines = licenseFailures.map(
+              (f) => `- \`${f.job}\` — ${f.step} ([logs](${f.url}))`
+            );
+            const body =
+              `### Task Summary\n\n` +
+              `License-binary drift on \`${context.sha.slice(0, 7)}\` ` +
+              `([run](${runUrl})).\n\n` +
+              `**Where:**\n\n` +
+              failureLines.join('\n') +
+              `\n\n**How to resolve:** refresh the per-module ` +
+              `\`LICENSE-binary\` file(s) to match the bundled versions.\n\n` +
+              `### Task Type\n\n` +
+              `- [ ] Refactor / Cleanup\n` +
+              `- [x] DevOps / Deployment / CI\n` +
+              `- [ ] Testing / QA\n` +
+              `- [ ] Documentation\n` +
+              `- [ ] Performance\n` +
+              `- [ ] Other\n\n` +
+              `---\n\n` +
+              `By submitting this issue, you agree to follow the ` +
+              `[Apache Code of Conduct]` +
+              `(https://www.apache.org/foundation/policies/conduct).`;
+
+            if (existing.length > 0) {
+              const issue = existing[0];
+              await github.rest.issues.update({
+                owner, repo, issue_number: issue.number,
+                title: TITLE, body,
+              });
+              core.info(`Updated tracking issue #${issue.number}`);
+              for (const extra of existing.slice(1)) {
+                await github.rest.issues.update({
+                  owner, repo, issue_number: extra.number,
+                  state: 'closed', state_reason: 'not_planned',
+                });
+                core.info(`Closed duplicate tracking issue #${extra.number}`);
+              }
+            } else {
+              const { data: created } = await github.rest.issues.create({
+                owner, repo, title: TITLE, body,
+                labels: [TRACKING_LABEL, 'ci', 'triage'],
+              });
+              core.info(`Filed new tracking issue #${created.number}`);
+            }

Reply via email to