Skip to content

chore(ci): Fail CI when a publishable crate is missing LICENSE or NOTICE - #3084

Open
dannycjones wants to merge 3 commits into
apache:mainfrom
dannycjones:check-license-and-notice
Open

chore(ci): Fail CI when a publishable crate is missing LICENSE or NOTICE#3084
dannycjones wants to merge 3 commits into
apache:mainfrom
dannycjones:check-license-and-notice

Conversation

@dannycjones

@dannycjones dannycjones commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

What changes are included in this PR?

This PR closes the gap where some crates were allowed to be published without LICENSE and NOTICE files. It does this by introducing a script that verifies the expected packaged artifact list and that the paths resolve to a real file

The checks are quite strict: they verify that the path is in the list, that it resolves to something non-empty, and that it resolves to the file in the root of the repository.

Are these changes tested?

Manual testing only.

AI Disclosure

Generative AI was used to author and test this change, with review and amendments before publishing the pull request.

Copilot AI lite review requested due to automatic review settings August 27, 2026 14:18
@dannycjones
dannycjones marked this pull request as draft August 27, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds a CI-enforced check ensuring all publishable workspace crates include LICENSE and NOTICE in their packaged artifacts, closing a gap where crates could be published missing required release files.

Changes:

  • Introduces dev/check_license_notice.sh to validate LICENSE/NOTICE presence in cargo package --list output and verify the paths resolve on disk.
  • Adds a make check-license-notice target and wires it into make check.
  • Updates CI to run the new check and to no longer exclude LICENSE/NOTICE changes from PR-triggered CI.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
dev/check_license_notice.sh New script to validate packaged artifacts contain LICENSE and NOTICE and that they resolve on disk.
Makefile Adds a dedicated target for the new script and includes it in the aggregate check target.
.github/workflows/ci.yml Ensures CI runs when LICENSE/NOTICE change and adds a step to run the new check.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread dev/check_license_notice.sh Outdated
Comment thread dev/check_license_notice.sh Outdated
Comment thread dev/check_license_notice.sh Outdated
Comment thread dev/check_license_notice.sh Outdated
@dannycjones

Copy link
Copy Markdown
Contributor Author

Nice, the check works. https://github.com/apache/iceberg-rust/actions/runs/33081511431/job/98549689753?pr=3084

Now I'll address the violations.

@dannycjones
dannycjones force-pushed the check-license-and-notice branch from 348df13 to 4d780e2 Compare August 27, 2026 15:30
@dannycjones
dannycjones requested a lite review from Copilot August 27, 2026 15:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 9 changed files in this pull request and generated 4 comments.

Comment thread Makefile Outdated
Comment thread dev/check_license_notice.sh
Comment thread dev/check_license_notice.sh
Comment thread dev/check_license_notice.sh
Missing LICENSE and NOTICE files for newly introduced crates (apache#2823) were
only discovered manually during release preparation, because nothing
asserted their presence.

Add dev/check_license_notice.sh, which lists the packaged files of every
publishable crate with `cargo package --list` and fails when a top-level
LICENSE or NOTICE is absent. Checking the packaged file list rather than
the crate directory also catches files present on disk but excluded from
the published crate. Because `cargo package --list` reports an entry even
when a symlinked target is gone, the file contents are checked separately
so a removed or emptied root copy fails too. Each crate's copy must also
resolve to the repository root file itself, so the license text cannot
drift from one crate to the next.

Run it from the CI lint job so pull requests fail when a crate is missing
either file, and stop excluding the root LICENSE and NOTICE from the
pull_request path filter, since every crate symlinks to those copies and a
PR removing them must still run the job. Also wire it into `make check` so
it can be run locally the same way as the other lint checks.

The check does not pass at this commit. It reports iceberg-property-macro,
which is published but ships neither file, and iceberg-catalog-s3tables,
whose LICENSE and NOTICE are real copies rather than the repository root
files. Both gaps are fixed in the two commits that follow.

Closes apache#2827
iceberg-property-macro is published to crates.io but shipped neither a
LICENSE nor a NOTICE, so the crate was distributed without the files an
Apache release requires. It was missed because the crate was added after
apache#1601, which introduced the files for the crates publishable at the time.

Add the same relative symlinks to the repository root copies that every
other publishable crate uses, which is what the new check in the preceding
commit reports.
Every publishable crate references the root LICENSE and NOTICE through a
relative symlink. That convention was established in apache#1601, which added the
files to the crates that were publishable at the time. `iceberg-catalog-s3tables`
was `publish = false` back then so it was skipped, and apache#1916 later added real
copies when the crate was made publishable. Every crate added since has followed
the symlink convention, leaving s3tables as the only outlier.

Replace the copies with symlinks so the root files remain the single source of
truth and per-crate copies cannot drift from them. This is also required for the
check added earlier in this branch to pass, which rejects a per-crate copy that
is not the root file itself: the s3tables copies are byte-identical to the root
files today, but nothing kept them that way.

`cargo package` dereferences the symlinks, so the published crate is unchanged:
LICENSE and NOTICE are still regular files with byte-identical content.
@dannycjones
dannycjones force-pushed the check-license-and-notice branch from 4d780e2 to 236dbbf Compare August 27, 2026 16:09
@dannycjones
dannycjones marked this pull request as ready for review August 27, 2026 16:09

while IFS=$'\t' read -r crate manifest_path; do
check_crate "${crate}" "$(dirname "${manifest_path}")" || FAILED=1
done < <(publishable_crates)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we do < <(publishable_crates), the function in a subshell, and the parent never inspects that subshell's exit code. So if the publishable_crates function fails (e.g. a bad Cargo.toml), this loop won't run and it will show up as a CI false green.

To fix this, we just need to capture it into a variable first before going into the loop. When we do that the function runs in the process that runs the main script.

Comment on lines +166 to +168
while IFS=$'\t' read -r crate manifest_path; do
check_crate "${crate}" "$(dirname "${manifest_path}")" || FAILED=1
done < <(publishable_crates)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
while IFS=$'\t' read -r crate manifest_path; do
check_crate "${crate}" "$(dirname "${manifest_path}")" || FAILED=1
done < <(publishable_crates)
crates="$(publishable_crates)"
if [ -z "${crates}" ]; then
report_error "No publishable crates found; cargo metadata may have failed."
exit 1
fi
while IFS=$'\t' read -r crate manifest_path; do
check_crate "${crate}" "$(dirname "${manifest_path}")" || FAILED=1
done <<<"${crates}"

@blackmwk blackmwk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The overall approach looks sound, but the false-green failure path in the new check needs to be fixed before merging.

Please address the unresolved review thread on dev/check_license_notice.sh:168: failures from publishable_crates are not propagated through the process substitution, so the loop can process no crates and the script can still succeed. Capturing and validating the command output before entering the loop would preserve the failure status and ensure that an empty crate list cannot silently pass.

I reproduced this behavior under set -Eeuo pipefail: the parent continued after the process-substitution producer returned a nonzero status.


This review was drafted by an AI-assisted tool and
confirmed by an Apache Iceberg Rust maintainer. After you've
addressed the points above and pushed an update, an Apache Iceberg Rust
maintainer — a real person — will take the next look
at the PR. The findings cite the project's review criteria;
if you think one of them is mis-applied, please reply on the
PR and a maintainer will weigh in.

More on how Apache Iceberg Rust handles maintainer review:
CONTRIBUTING.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add checks to CI / RC verify scripts for presence of LICENSE and NOTICE in all crates

4 participants