From db69d55b57c79d2efb1a1aa1d64bb6ddc940e4bf Mon Sep 17 00:00:00 2001 From: Ridha Chahed Date: Wed, 24 Jun 2026 16:03:39 +0200 Subject: [PATCH] Add open source contribution workflows --- .github/CODEOWNERS | 19 ++++ .github/ISSUE_TEMPLATE/bug_report.yml | 34 +++++++ .github/ISSUE_TEMPLATE/config.yml | 8 ++ .github/ISSUE_TEMPLATE/feature_request.yml | 20 ++++ .github/PULL_REQUEST_TEMPLATE.md | 37 +++++++ .github/labeler.yml | 54 ++++++++++ .github/workflows/assign-reviewers.yml | 95 ++++++++++++++++++ .github/workflows/clang-format.yml | 27 +++++ .github/workflows/codex-pr-review.yml | 63 ++++++++++++ .github/workflows/labeler.yml | 49 +++++++++ .github/workflows/mark-integrate.yml | 38 +++++++ .github/workflows/mtr-smoke.yml | 38 +++++++ .github/workflows/pr-build.yml | 49 +++++++++ .github/workflows/pr-template-check.yml | 60 +++++++++++ .github/workflows/stale.yml | 20 ++++ CONTRIBUTING.md | 111 +++++++++++++++++++++ Docs/development/BUILD-FROM-SOURCE.md | 48 +++++++++ Docs/development/open-development-model.md | 83 +++++++++++++++ Docs/rfcs/0000-template.md | 32 ++++++ Docs/rfcs/README.md | 18 ++++ mysql-test/collections/smoke.list | 10 ++ scripts/dev/bootstrap.sh | 15 +++ scripts/dev/build.sh | 29 ++++++ scripts/dev/demo-labeler.sh | 63 ++++++++++++ scripts/dev/format.sh | 14 +++ scripts/dev/mtr.sh | 24 +++++ 26 files changed, 1058 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/assign-reviewers.yml create mode 100644 .github/workflows/clang-format.yml create mode 100644 .github/workflows/codex-pr-review.yml create mode 100644 .github/workflows/labeler.yml create mode 100644 .github/workflows/mark-integrate.yml create mode 100644 .github/workflows/mtr-smoke.yml create mode 100644 .github/workflows/pr-build.yml create mode 100644 .github/workflows/pr-template-check.yml create mode 100644 .github/workflows/stale.yml create mode 100644 CONTRIBUTING.md create mode 100644 Docs/development/BUILD-FROM-SOURCE.md create mode 100644 Docs/development/open-development-model.md create mode 100644 Docs/rfcs/0000-template.md create mode 100644 Docs/rfcs/README.md create mode 100644 mysql-test/collections/smoke.list create mode 100755 scripts/dev/bootstrap.sh create mode 100755 scripts/dev/build.sh create mode 100755 scripts/dev/demo-labeler.sh create mode 100755 scripts/dev/format.sh create mode 100755 scripts/dev/mtr.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..2df4271967 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,19 @@ +# Review routing + basis for the external committer write model described in +# docs/development/open-development-model.md. +# +# Currently all paths are owned by @mysql-admin. As the model rolls out, replace +# the catch-all with per-area teams (the structure below shows the intended split). + +# Default owner for everything not matched more specifically: +* @mysql-admin + +# Intended per-area split (all @mysql-admin for now): +/storage/innobase/ @mysql-admin +/sql/join_optimizer/ @mysql-admin +/sql/rpl_* @mysql-admin +/plugin/group_replication/ @mysql-admin +/client/ @mysql-admin +/cmake/ @mysql-admin +/.github/ @mysql-admin +/scripts/dev/ @mysql-admin +/docs/ @mysql-admin diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..44f3858789 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,34 @@ +name: Bug report +description: Report incorrect or unexpected server behavior +labels: ["bug", "needs-triage"] +body: + - type: markdown + attributes: + value: | + Security vulnerabilities must **not** be filed here. Report them through + . + - type: input + id: version + attributes: + label: MySQL version / commit + placeholder: "9.x trunk @ , or 8.4.x" + validations: { required: true } + - type: textarea + id: repro + attributes: + label: Steps to reproduce + description: Minimal SQL or MTR case if possible. + placeholder: | + CREATE TABLE t (...); + ... + validations: { required: true } + - type: textarea + id: expected + attributes: + label: Expected vs actual result + validations: { required: true } + - type: input + id: platform + attributes: + label: Platform / compiler + placeholder: "Ubuntu 24.04, gcc 13" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..76609cfcf8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Security vulnerability + url: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/ + about: Report security issues privately through Oracle's process — never as a public issue. + - name: Usage questions / general help + url: https://forums.mysql.com/ + about: For "how do I…" questions, please use the community forums. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..7fc4a1105f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,20 @@ +name: Feature request +description: Propose an enhancement +labels: ["feature-request", "needs-triage"] +body: + - type: textarea + id: problem + attributes: + label: Problem / use case + validations: { required: true } + - type: textarea + id: proposal + attributes: + label: Proposed behavior + validations: { required: true } + - type: checkboxes + id: scope + attributes: + label: Scope + options: + - label: This changes SQL syntax, on-disk format, or public behavior (an RFC will be needed) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..476d79f6eb --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,37 @@ + + +### What does this change do? + + + +### Why is it needed? + +### How was it tested? + +- [ ] Added/updated MTR tests under `mysql-test/` +- [ ] `scripts/dev/mtr.sh smoke` passes locally +- [ ] Ran the relevant full suite (name it): ______ + +### Contributor checklist + +- [ ] I have signed the [OCA](https://oca.opensource.oracle.com) with the email on these commits +- [ ] I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it. +- [ ] Code is formatted (`scripts/dev/format.sh`) +- [ ] Commits are focused with descriptive messages +- [ ] For a behavior/format/syntax change, an RFC was opened or linked: ______ + +### AI assistance + +- [ ] I did not use AI assistance for this contribution +- [ ] I used AI assistance for this contribution + +If AI assistance was used, describe the tool(s) and extent of use: + + + +### Areas touched + + diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000..1fd4c198b3 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,54 @@ +# Area auto-labels, driven by the paths a PR touches. Keeps triage cheap and +# routes reviews to the right owners (see CODEOWNERS). +# +# Format note: actions/labeler@v5 requires the `changed-files` / +# `any-glob-to-any-file` structure below. The older flat "label: [globs]" +# layout (v4) is NOT compatible with v5 and fails to parse. + +"innodb": + - changed-files: + - any-glob-to-any-file: ["storage/innobase/**"] + +"optimizer": + - changed-files: + - any-glob-to-any-file: + - "sql/join_optimizer/**" + - "sql/sql_optimizer*" + - "sql/range_optimizer/**" + +"replication": + - changed-files: + - any-glob-to-any-file: + - "sql/rpl_*" + - "libbinlogevents/**" + - "plugin/group_replication/**" + +"client": + - changed-files: + - any-glob-to-any-file: + - "client/**" + - "libmysql/**" + +"pluggable": + - changed-files: + - any-glob-to-any-file: + - "plugin/**" + - "components/**" + +"build": + - changed-files: + - any-glob-to-any-file: + - "cmake/**" + - "CMakeLists.txt" + - "scripts/dev/**" + - ".github/**" + +"tests": + - changed-files: + - any-glob-to-any-file: ["mysql-test/**"] + +"docs": + - changed-files: + - any-glob-to-any-file: + - "docs/**" + - "**/*.md" diff --git a/.github/workflows/assign-reviewers.yml b/.github/workflows/assign-reviewers.yml new file mode 100644 index 0000000000..e599c40d32 --- /dev/null +++ b/.github/workflows/assign-reviewers.yml @@ -0,0 +1,95 @@ +name: Assign Reviewers + +on: + pull_request: + types: [labeled, synchronize, reopened, ready_for_review] + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + assign: + if: ${{ contains(github.event.pull_request.labels.*.name, 'OCA_ready') && github.event.pull_request.draft == false }} + runs-on: ubuntu-24.04 + steps: + - name: Assign reviewers from touched paths + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + + const labelSpecs = [ + { name: 'OCA_ready', color: '0E8A16', description: 'Contributor agreement is ready for reviewer assignment' }, + { name: 'integrate', color: '5319E7', description: 'Approved patch ready for integration' }, + ]; + + for (const label of labelSpecs) { + try { + await github.rest.issues.getLabel({ ...context.repo, name: label.name }); + await github.rest.issues.updateLabel({ ...context.repo, ...label }); + } catch (error) { + if (error.status !== 404) throw error; + await github.rest.issues.createLabel({ ...context.repo, ...label }); + } + } + + for (const staleLabel of ['oca:verified', 'oca:unsigned']) { + try { + await github.rest.issues.removeLabel({ + ...context.repo, + issue_number: pr.number, + name: staleLabel, + }); + } catch (error) { + if (error.status !== 404) throw error; + } + } + + const files = await github.paginate(github.rest.pulls.listFiles, { + ...context.repo, + pull_number: pr.number, + per_page: 100, + }); + + const reviewers = new Set(['mysql-admin']); + const routes = [ + { pattern: /^storage\/innobase\//, reviewers: ['mysql-admin'] }, + { pattern: /^sql\/join_optimizer\//, reviewers: ['mysql-admin'] }, + { pattern: /^sql\/rpl_/, reviewers: ['mysql-admin'] }, + { pattern: /^plugin\/group_replication\//, reviewers: ['mysql-admin'] }, + { pattern: /^client\//, reviewers: ['mysql-admin'] }, + { pattern: /^cmake\//, reviewers: ['mysql-admin'] }, + { pattern: /^\.github\//, reviewers: ['mysql-admin'] }, + { pattern: /^scripts\/dev\//, reviewers: ['mysql-admin'] }, + { pattern: /(^|\/)docs?\//i, reviewers: ['mysql-admin'] }, + ]; + + for (const file of files) { + for (const route of routes) { + if (route.pattern.test(file.filename)) { + for (const reviewer of route.reviewers) reviewers.add(reviewer); + } + } + } + + const requestable = [...reviewers].filter((reviewer) => reviewer.toLowerCase() !== pr.user.login.toLowerCase()); + if (requestable.length === 0) { + core.info(`No reviewer request sent because all routed reviewers are the PR author (${pr.user.login}).`); + return; + } + + await github.rest.pulls.requestReviewers({ + ...context.repo, + pull_number: pr.number, + reviewers: requestable, + }); + + await github.rest.issues.addLabels({ + ...context.repo, + issue_number: pr.number, + labels: ['OCA_ready'], + }); + + core.info(`Requested reviewers: ${requestable.join(', ')}`); diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 0000000000..fb1b546eb8 --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,27 @@ +name: Format Check +on: + pull_request: + paths: ["**/*.c", "**/*.cc", "**/*.cpp", "**/*.h", "**/*.hpp"] +jobs: + clang-format: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge + fetch-depth: 2 + - name: Install clang-format + run: sudo apt-get update && sudo apt-get install -y clang-format-18 + # Uses the repository's existing .clang-format — no style is redefined here. + - name: Check formatting of changed files + run: | + changed=$(git diff --name-only HEAD^1 HEAD | grep -E '\.(c|cc|cpp|h|hpp)$' || true) + [ -z "$changed" ] && { echo "No C/C++ changes."; exit 0; } + fail=0 + for f in $changed; do + [ -f "$f" ] || continue + if ! clang-format-18 --style=file --dry-run --Werror "$f"; then fail=1; fi + done + if [ "$fail" -ne 0 ]; then + echo "::error::Run scripts/dev/format.sh to fix formatting."; exit 1 + fi diff --git a/.github/workflows/codex-pr-review.yml b/.github/workflows/codex-pr-review.yml new file mode 100644 index 0000000000..741db82267 --- /dev/null +++ b/.github/workflows/codex-pr-review.yml @@ -0,0 +1,63 @@ +name: Codex PR Review + +on: + # Disabled for now. Re-enable pull_request triggers when the repo is ready to + # run Codex review automatically. + workflow_dispatch: + +permissions: + contents: read + +jobs: + codex: + if: github.event.pull_request.draft == false + runs-on: ubuntu-24.04 + timeout-minutes: 20 + permissions: + contents: read + issues: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + ref: refs/pull/${{ github.event.pull_request.number }}/merge + fetch-depth: 2 + persist-credentials: false + + - name: Summarize and review with Codex + id: run_codex + uses: openai/codex-action@v1 + with: + openai-api-key: ${{ secrets.OPENAI_API_KEY }} + sandbox: read-only + prompt: | + This is PR #${{ github.event.pull_request.number }} for ${{ github.repository }}. + + Summarize the changes and review only the diff introduced by this PR: + git diff --stat HEAD^1 HEAD + git diff HEAD^1 HEAD + + Return: + 1. A concise change summary. + 2. High-confidence review findings, with file paths and line references when possible. + 3. Any test gaps or risks. + + Pull request title and body: + ---- + ${{ github.event.pull_request.title }} + ${{ github.event.pull_request.body }} + + - name: Post Codex feedback + if: steps.run_codex.outputs['final-message'] != '' + uses: actions/github-script@v7 + env: + CODEX_FINAL_MESSAGE: ${{ steps.run_codex.outputs['final-message'] }} + with: + github-token: ${{ github.token }} + script: | + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: process.env.CODEX_FINAL_MESSAGE, + }); diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000000..926432f0a6 --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,49 @@ +name: Auto Label + +# TODO: CHANGE BEFORE MERGE -> revert the trigger below to `pull_request_target`. +# +# `pull_request_target` is what the real upstream needs: it runs in the context +# of the BASE repo and hands the job a write-scoped token even for PRs opened +# from forks, so external contributors' PRs can still be labeled. It is set to +# `pull_request` here only for this fork PoC because: +# 1) a `pull_request_target` workflow must already exist on the base branch +# (trunk) to run at all, which it does not on this very first PR; and +# 2) this PR is same-repo (head and base both on RidhaOracle/mysql-server), +# so `pull_request` already carries a write token and can apply labels. +on: [pull_request] # TODO: CHANGE BEFORE MERGE -> [pull_request_target] + +permissions: { contents: read, pull-requests: write, issues: write } + +jobs: + label: + runs-on: ubuntu-24.04 + steps: + - name: Ensure labels have colors + uses: actions/github-script@v7 + with: + script: | + const labels = [ + { name: 'innodb', color: '1D76DB', description: 'Changes touching InnoDB storage engine code' }, + { name: 'optimizer', color: '5319E7', description: 'Changes touching optimizer code' }, + { name: 'replication', color: '0052CC', description: 'Changes touching replication or binlog code' }, + { name: 'client', color: '0E8A16', description: 'Changes touching client or libmysql code' }, + { name: 'pluggable', color: 'FBCA04', description: 'Changes touching plugins or components' }, + { name: 'build', color: 'D93F0B', description: 'Changes touching build or GitHub automation' }, + { name: 'tests', color: 'BFDADC', description: 'Changes touching test code or test data' }, + { name: 'docs', color: '0075CA', description: 'Changes touching documentation' }, + { name: 'OCA_ready', color: '0E8A16', description: 'Contributor agreement is ready for reviewer assignment' }, + { name: 'integrate', color: '5319E7', description: 'Approved patch ready for integration' }, + ]; + + for (const label of labels) { + try { + await github.rest.issues.getLabel({ ...context.repo, name: label.name }); + await github.rest.issues.updateLabel({ ...context.repo, ...label }); + } catch (error) { + if (error.status !== 404) throw error; + await github.rest.issues.createLabel({ ...context.repo, ...label }); + } + } + + - uses: actions/labeler@v5 + with: { sync-labels: true } diff --git a/.github/workflows/mark-integrate.yml b/.github/workflows/mark-integrate.yml new file mode 100644 index 0000000000..ad85894233 --- /dev/null +++ b/.github/workflows/mark-integrate.yml @@ -0,0 +1,38 @@ +name: Mark Integrate + +on: + pull_request_review: + types: [submitted] + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + integrate: + if: ${{ github.event.review.state == 'approved' }} + runs-on: ubuntu-24.04 + steps: + - name: Label approved patch for integration + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const label = { name: 'integrate', color: '5319E7', description: 'Approved patch ready for integration' }; + + try { + await github.rest.issues.getLabel({ ...context.repo, name: label.name }); + await github.rest.issues.updateLabel({ ...context.repo, ...label }); + } catch (error) { + if (error.status !== 404) throw error; + await github.rest.issues.createLabel({ ...context.repo, ...label }); + } + + await github.rest.issues.addLabels({ + ...context.repo, + issue_number: pr.number, + labels: [label.name], + }); + + core.info(`Marked PR #${pr.number} as integrate after approval by ${context.payload.review.user.login}.`); diff --git a/.github/workflows/mtr-smoke.yml b/.github/workflows/mtr-smoke.yml new file mode 100644 index 0000000000..c027912040 --- /dev/null +++ b/.github/workflows/mtr-smoke.yml @@ -0,0 +1,38 @@ +name: MTR Smoke +on: + pull_request: + paths-ignore: ["docs/**", "**/*.md"] +concurrency: + group: mtr-${{ github.event.pull_request.number }} + cancel-in-progress: true + +# Fast, curated test signal on every PR. The full suite (innodb, replication, +# rpl, group_replication, …) runs nightly on trunk, not per-PR, to keep the +# contributor feedback loop under ~20 minutes. +jobs: + smoke: + runs-on: ubuntu-24.04-8core + needs: [] + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - run: scripts/dev/bootstrap.sh + - uses: actions/cache@v4 + with: + path: ~/.cache/mysql-boost + key: boost-${{ hashFiles('cmake/boost.cmake') }} + - uses: actions/cache@v4 + with: + path: ~/.cache/ccache + key: ccache-gcc-${{ github.sha }} + restore-keys: ccache-gcc- + - run: scripts/dev/build.sh debug + - name: Run smoke suite + run: scripts/dev/mtr.sh smoke + - name: Publish test report + if: always() + uses: actions/upload-artifact@v4 + with: + name: mtr-smoke-logs + path: build/mysql-test/var/log/ + retention-days: 5 diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml new file mode 100644 index 0000000000..3cb2f54e69 --- /dev/null +++ b/.github/workflows/pr-build.yml @@ -0,0 +1,49 @@ +name: PR Build +on: + pull_request: + paths-ignore: ["docs/**", "**/*.md"] + +# Cancel superseded runs so a fast push doesn't queue behind a stale build. +concurrency: + group: build-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + build: + name: Debug build (${{ matrix.compiler }}) + runs-on: ubuntu-24.04-8core + timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + compiler: [gcc, clang] + env: + CC: ${{ matrix.compiler == 'gcc' && 'gcc' || 'clang' }} + CXX: ${{ matrix.compiler == 'gcc' && 'g++' || 'clang++' }} + steps: + - uses: actions/checkout@v4 + - name: Install toolchain + run: scripts/dev/bootstrap.sh + # Boost is large and version-pinned: cache it so most runs skip the download. + - name: Cache Boost + uses: actions/cache@v4 + with: + path: ~/.cache/mysql-boost + key: boost-${{ hashFiles('cmake/boost.cmake') }} + # ccache turns the second build of a PR from ~50 min into a few minutes. + - name: Cache ccache + uses: actions/cache@v4 + with: + path: ~/.cache/ccache + key: ccache-${{ matrix.compiler }}-${{ github.sha }} + restore-keys: ccache-${{ matrix.compiler }}- + - name: Show runner resources + run: | + echo "CPU cores: $(nproc)" + free -h + df -h . + - name: Build + run: scripts/dev/build.sh debug + - name: Show ccache stats + if: always() + run: ccache --show-stats diff --git a/.github/workflows/pr-template-check.yml b/.github/workflows/pr-template-check.yml new file mode 100644 index 0000000000..5ce0f6b4dc --- /dev/null +++ b/.github/workflows/pr-template-check.yml @@ -0,0 +1,60 @@ +name: PR Template Check + +on: + pull_request: + paths: + - ".github/PULL_REQUEST_TEMPLATE.md" + - ".github/workflows/pr-template-check.yml" + +permissions: + contents: read + +jobs: + template: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - name: Verify suggested pull request template + run: | + cat > expected-pr-template.md <<'EOF' + + + ### What does this change do? + + + + ### Why is it needed? + + ### How was it tested? + + - [ ] Added/updated MTR tests under `mysql-test/` + - [ ] `scripts/dev/mtr.sh smoke` passes locally + - [ ] Ran the relevant full suite (name it): ______ + + ### Contributor checklist + + - [ ] I have signed the [OCA](https://oca.opensource.oracle.com) with the email on these commits + - [ ] I confirm the code being submitted is offered under the terms of the OCA, and that I am authorized to contribute it. + - [ ] Code is formatted (`scripts/dev/format.sh`) + - [ ] Commits are focused with descriptive messages + - [ ] For a behavior/format/syntax change, an RFC was opened or linked: ______ + + ### AI assistance + + - [ ] I did not use AI assistance for this contribution + - [ ] I used AI assistance for this contribution + + If AI assistance was used, describe the tool(s) and extent of use: + + + + ### Areas touched + + + EOF + perl -pi -e 's/^ //' expected-pr-template.md + diff -u expected-pr-template.md .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..81fa72cf6b --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,20 @@ +name: Triage Hygiene +on: + schedule: [{ cron: "0 6 * * 1-5" }] # weekday mornings +permissions: { issues: write, pull-requests: write } + +# Gentle: only nudges items explicitly waiting on the author for info. Nothing +# labeled good-first-issue, help-wanted, or under active review is ever touched. +jobs: + stale: + runs-on: ubuntu-24.04 + steps: + - uses: actions/stale@v9 + with: + only-labels: "needs-info" + days-before-stale: 21 + days-before-close: 14 + stale-issue-message: "Marking as stale: still waiting on the requested info. Comment to keep it open." + stale-pr-message: "Marking as stale: waiting on requested changes. Push or comment to keep it open." + exempt-pr-labels: "good first issue,help wanted,under-review" + exempt-issue-labels: "good first issue,help wanted,roadmap" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..6263ef430e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,111 @@ +# Contributing to MySQL Server + +We welcome your code contributions. This guide gets you from a fresh clone to a +merged pull request with as little friction as possible. + +> **TL;DR** — Sign the [OCA](https://oca.opensource.oracle.com), run +> `scripts/dev/bootstrap.sh`, make your change, run `scripts/dev/mtr.sh smoke`, +> and open a PR. CI builds your branch and runs a smoke suite automatically. Once +> OCA readiness is confirmed, maintainers apply `OCA_ready` and reviewer routing +> requests the right reviewer. + +--- + +## 1. Sign the Oracle Contributor Agreement (once) + +Before any contribution can be merged you must have signed the +[Oracle Contributor Agreement (OCA)](https://oca.opensource.oracle.com). + +1. Create or reuse a user account at . +2. Sign the OCA, referencing that account. +3. Use the **same email** on your Git commits (`git config user.email`). + +For now, OCA readiness is confirmed by maintainers rather than by an automated +pipeline. After verification, a maintainer applies the `OCA_ready` label. That +label triggers reviewer assignment based on the files touched by the PR. + +## 2. Get a build in one command + +Full instructions live in [`docs/development/BUILD-FROM-SOURCE.md`](docs/development/BUILD-FROM-SOURCE.md). +The fast path: + +```bash +# Reproducible toolchain + Boost, identical to CI: +scripts/dev/bootstrap.sh # installs/pins deps +scripts/dev/build.sh debug # configures + builds into build/ +``` + +These scripts pin the same compiler, CMake, Ninja, Boost, and test tooling used +by CI, so "works locally" tracks "passes in CI." + +## 3. Find something to work on + +- Issues labeled [`good first issue`](../../labels/good%20first%20issue) are + scoped, have reproduction steps, and a named area owner. +- [`help wanted`](../../labels/help%20wanted) marks larger items the team would + welcome help on. +- For a substantial change (new syntax, on-disk format, public behavior), open a + short **RFC** first — see [`docs/rfcs/`](docs/rfcs/). This avoids investing in a + branch that conflicts with internal direction, the single most common reason + external work has historically stalled. + +## 4. Make the change + +- Match existing style; formatting is enforced by `.clang-format`. Run + `scripts/dev/format.sh` (or install the pre-commit hook below) so you never get + a review comment about whitespace. +- Add or update tests. Every behavior change ships with MTR coverage under + `mysql-test/`. See `docs/development/BUILD-FROM-SOURCE.md#running-tests`. +- Keep commits focused and write a clear message body explaining *why*. + +Optional but recommended — install the format pre-commit hook: + +```bash +ln -s ../../scripts/dev/format.sh .git/hooks/pre-commit +``` + +## 5. Run tests locally (the same ones CI runs) + +```bash +scripts/dev/mtr.sh smoke # ~5 min curated subset, mirrors the PR check +scripts/dev/mtr.sh main # full main suite +scripts/dev/mtr.sh --suite=innodb # pass MTR args straight through +``` + +## 6. Open the pull request + +Push your branch and open a PR against `trunk`. The +[pull request template](.github/PULL_REQUEST_TEMPLATE.md) prompts for the few +things reviewers always need. On open, CI automatically: + +- checks formatting, +- builds Debug on gcc and clang, +- runs the smoke MTR suite, +- auto-labels the affected area. + +After OCA readiness is confirmed, maintainers apply `OCA_ready`; GitHub then +requests the routed reviewer. Once the reviewer approves the PR, automation adds +the `integrate` label. + +You should see green (or actionable red) within ~20 minutes — without asking +anyone. + +## What to expect from us (triage SLAs) + +These are the response targets the maintainers hold themselves to. They are +published here so the contract is mutual and visible: + +| Stage | Target | +|-----------------------------------------|-------------------| +| First maintainer response on a new PR | 3 business days | +| First response on a `good first issue` | 2 business days | +| Review round-trip after you push | 5 business days | +| Decision on an RFC | 15 business days | + +If a PR goes quiet past these windows, ping `@mysql/triage` on the thread. + +## Alternative submission path + +You may still attach a patch to a bug record at via the +*contribution* tab. GitHub pull requests are now the recommended path because +they get automated CI feedback and public review history. diff --git a/Docs/development/BUILD-FROM-SOURCE.md b/Docs/development/BUILD-FROM-SOURCE.md new file mode 100644 index 0000000000..03ca36a704 --- /dev/null +++ b/Docs/development/BUILD-FROM-SOURCE.md @@ -0,0 +1,48 @@ +# Building MySQL Server from source + +The canonical reference is . +This page is the *fast path* for contributors and is kept in sync with what CI +runs, so following it reproduces the automated checks locally. + +## Native Ubuntu 24.04 + +```bash +scripts/dev/bootstrap.sh # toolchain + libs +scripts/dev/build.sh debug # configure (Ninja) + build into build/ +``` + +`build.sh` invokes CMake with `-DDOWNLOAD_BOOST=1 -DWITH_BOOST=~/.cache/mysql-boost`, +so the version-pinned Boost is downloaded once and reused. Use `release` for a +`RelWithDebInfo` build. + +### What the script runs under the hood + +```bash +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DDOWNLOAD_BOOST=1 -DWITH_BOOST=~/.cache/mysql-boost \ + -DWITH_UNIT_TESTS=ON +cmake --build build -j"$(nproc)" +``` + +## Running tests + +MySQL uses **MTR** (MySQL Test Run) under `mysql-test/`. + +```bash +scripts/dev/mtr.sh smoke # curated fast subset — the PR check +scripts/dev/mtr.sh main # full main suite +scripts/dev/mtr.sh --suite=innodb # any suite; args pass straight to ./mtr +``` + +Logs land in `build/mysql-test/var/log/`. The smoke list lives at +`mysql-test/collections/smoke.list` and is what the `MTR Smoke` PR workflow runs, +so a green local smoke run predicts a green PR check. + +## Troubleshooting + +| Symptom | Fix | +|--------------------------------------|-----------------------------------------------------------| +| Boost version error at configure | delete `~/.cache/mysql-boost` and re-run `build.sh` | +| `bison: command not found` | re-run `scripts/dev/bootstrap.sh` | +| Slow rebuilds | confirm ccache is on the PATH; `ccache -s` shows hit rate | diff --git a/Docs/development/open-development-model.md b/Docs/development/open-development-model.md new file mode 100644 index 0000000000..9156ecbf48 --- /dev/null +++ b/Docs/development/open-development-model.md @@ -0,0 +1,83 @@ +# Open development model (proposed) + +> Status: **proposal / horizon 3** — engineering working model, not a governance +> or licensing change. The license (GPLv2) and the OCA are unchanged. + +This document describes the target development workflow the earlier horizons +build toward. The single organizing idea is **directionality**. + +## The directionality principle + +Today MySQL's public GitHub tree is effectively a **downstream mirror**: canonical +development happens internally and lands on the public tree on a publish cadence. +External pull requests are re-keyed into an internal tracker before they can be +acted on. The public tree being downstream is the root cause of most contributor +friction — invisible CI, delayed review, merge windows that don't line up with +where the internal tree already moved. + +The proposal is to **invert the arrow**: + +``` + CURRENT (public tree downstream) TARGET (public tree upstream) + + internal trunk ──► publish ──► public trunk (canonical) + ▲ │ + external PR ─► re-key ─► internal ├─► security embargo branch (downstream) + ├─► commercial LTS trains (downstream) + └─► closed perf/CI infra (downstream) +``` + +The public tree becomes the **upstream of record**. Internal work does not +disappear — it becomes a set of **bounded, well-defined downstream areas** that +consume the public tree rather than feed it. + +## This is the OpenJDK model + +OpenJDK demonstrates that a commercially sponsored project can run public-upstream +while still accommodating legitimate internal-only workflows. The public tree is +canonical; vendor-internal work (security embargoes, commercial LTS trains, closed +test/perf infrastructure) sits downstream in bounded areas. MySQL inverts this +relationship today; this proposal aligns it with the OpenJDK pattern without any +change to license or contributor agreement. + +## Legitimate downstream areas (stay internal, by design) + +Directionality does not mean "everything is public." These remain internal, and +that is correct: + +1. **Security embargoes.** CVE work happens on a private branch until coordinated + disclosure, then merges to public trunk. (Same as OpenJDK's vulnerability group.) +2. **Commercial / LTS release trains.** Enterprise-only backports and packaging + are downstream consumers of public trunk, not a parallel source of truth. +3. **Closed performance and test infrastructure.** Large internal benchmarking and + hardware-specific CI can gate releases without being the canonical tree. + +The boundary is explicit: anything *not* in those three categories develops in +the open by default. + +## External committer write model + +With the public tree canonical, trusted external contributors can earn commit +access to defined areas (mirroring OpenJDK Committer/Reviewer roles), bounded by +`CODEOWNERS`: + +- **Contributor** — opens PRs (today's ceiling for external participants). +- **Area committer** — merge rights within specific paths after a track record; + changes still require a maintainer review and green CI. +- **Maintainer/Reviewer** — owns an area, reviews, holds the triage SLAs in + `CONTRIBUTING.md`. + +This is the row your benchmark matrix calls "external committer write model" — +the capability MySQL currently lacks and PostgreSQL, MariaDB, OpenJDK, and +Valkey all have. + +## How the horizons ladder up to this + +| Horizon | Change | Why it's a prerequisite for directionality | +|---|---|---| +| 1 | GitHub-native CONTRIBUTING, templates, in-repo build guide | Makes the public tree usable as the entry point | +| 2 | PR CI (build + smoke MTR), local build scripts, OCA readiness gate, auto-routing | Gives the public tree a real, fast feedback loop — the thing a canonical tree must have | +| 3 | RFC process, external committer model, bounded downstream areas | Completes the inversion: public trunk becomes upstream of record | + +You cannot flip directionality without horizons 1–2 first: a canonical tree that +can't give contributors automated feedback isn't actually canonical. diff --git a/Docs/rfcs/0000-template.md b/Docs/rfcs/0000-template.md new file mode 100644 index 0000000000..2d9e8492a9 --- /dev/null +++ b/Docs/rfcs/0000-template.md @@ -0,0 +1,32 @@ +# RFC 0000: + +- Start date: YYYY-MM-DD +- Author(s): <name / handle> +- Status: Draft | In review | Accepted | Rejected | Superseded +- Tracking issue: # + +## Summary + +One paragraph: what changes and who is affected. + +## Motivation + +The problem. Why the status quo is insufficient. Who hits it. + +## Guide-level explanation + +Explain as if teaching a user/contributor: new syntax, behavior, defaults, +migration. Include examples. + +## Reference-level explanation + +Design detail: on-disk/format impact, optimizer/replication/protocol effects, +compatibility, performance, security. How it's tested (MTR coverage). + +## Drawbacks & alternatives + +Why we might not do this; what else was considered and why this wins. + +## Unresolved questions + +What must be settled before/while implementing. diff --git a/Docs/rfcs/README.md b/Docs/rfcs/README.md new file mode 100644 index 0000000000..e491ad8bb0 --- /dev/null +++ b/Docs/rfcs/README.md @@ -0,0 +1,18 @@ +# MySQL RFCs + +Substantial changes — new SQL syntax, on-disk format changes, protocol changes, +or anything altering public behavior — start with an RFC. This exists so external +contributors learn early whether a direction aligns with internal plans, instead +of discovering a conflict after writing the code. That mismatch is the single +most common reason external work has historically stalled. + +## Process + +1. Copy `0000-template.md` to `NNNN-short-title.md` (next free number). +2. Open a PR adding it; discussion happens on the PR. +3. A maintainer records the decision within the RFC SLA in `CONTRIBUTING.md` + (target: 15 business days). +4. Accepted RFCs merge with status `Accepted` and a tracking issue; implementation + PRs reference the RFC. + +Small, behavior-preserving fixes do **not** need an RFC — just open a PR. diff --git a/mysql-test/collections/smoke.list b/mysql-test/collections/smoke.list new file mode 100644 index 0000000000..7504a928d9 --- /dev/null +++ b/mysql-test/collections/smoke.list @@ -0,0 +1,10 @@ +# Curated fast PR signal — one representative test per major area. +# Run via: scripts/dev/mtr.sh smoke (also the MTR Smoke PR workflow) +main.select +main.join +main.subselect +main.create +main.alter_table +main.grant +main.type_decimal +main.func_str diff --git a/scripts/dev/bootstrap.sh b/scripts/dev/bootstrap.sh new file mode 100755 index 0000000000..c97d9e0e69 --- /dev/null +++ b/scripts/dev/bootstrap.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Install/pin the build toolchain so a local build matches what reviewers' +# automation sees. Tested on Ubuntu 24.04. +set -euo pipefail + +SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo" + +$SUDO apt-get update +$SUDO apt-get install -y --no-install-recommends \ + build-essential cmake ninja-build pkg-config bison \ + libssl-dev libncurses-dev libldap2-dev libsasl2-dev libcurl4-openssl-dev libtirpc-dev \ + ccache git curl + +echo "Toolchain ready. Boost is fetched on first configure via -DDOWNLOAD_BOOST=1." +echo "Next: scripts/dev/build.sh debug" diff --git a/scripts/dev/build.sh b/scripts/dev/build.sh new file mode 100755 index 0000000000..16441448cb --- /dev/null +++ b/scripts/dev/build.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# One-command configure + build. Usage: build.sh [debug|release] [extra cmake args] +# Keeps Boost out-of-tree and cached so re-clones don't re-download it. +set -euo pipefail + +BUILD_TYPE="${1:-debug}"; shift || true +case "$BUILD_TYPE" in + debug) CMAKE_BT=Debug ;; + release) CMAKE_BT=RelWithDebInfo ;; + *) echo "usage: build.sh [debug|release] [extra cmake args]"; exit 2 ;; +esac + +REPO_ROOT="$(git rev-parse --show-toplevel)" +BOOST_DIR="${MYSQL_BOOST_DIR:-$HOME/.cache/mysql-boost}" +BUILD_DIR="${BUILD_DIR:-$REPO_ROOT/build}" +mkdir -p "$BOOST_DIR" + +export CCACHE_DIR="${CCACHE_DIR:-$HOME/.cache/ccache}" + +cmake -S "$REPO_ROOT" -B "$BUILD_DIR" -G Ninja \ + -DCMAKE_BUILD_TYPE="$CMAKE_BT" \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DDOWNLOAD_BOOST=1 -DWITH_BOOST="$BOOST_DIR" -DDOWNLOAD_BOOST_TIMEOUT=600 \ + -DWITH_UNIT_TESTS=ON \ + "$@" + +cmake --build "$BUILD_DIR" -j "$(nproc)" +echo "Built into $BUILD_DIR" diff --git a/scripts/dev/demo-labeler.sh b/scripts/dev/demo-labeler.sh new file mode 100755 index 0000000000..0933af28c0 --- /dev/null +++ b/scripts/dev/demo-labeler.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# demo-labeler.sh — exercise the PR auto-labeler for a stakeholder showcase. +# +# Appends one clearly-marked throwaway comment to a real file in each labeled +# area, so a single push lights up every "area:*" label on the PR. This is a +# DEMO commit: revert it before merge (see the printed instructions at the end). +# +# Usage (run from a full mysql-server checkout, on your PR branch): +# bash scripts/dev/demo-labeler.sh # stage + commit the demo edits +# bash scripts/dev/demo-labeler.sh --push # ...and push to origin +set -euo pipefail + +MARK="OCA/labeler demo — revert before merge" + +# label -> glob (mirrors .github/labeler.yml). One representative file each. +globs=( + "storage/innobase/**:area:innodb" + "sql/sql_optimizer*:area:optimizer" + "sql/rpl_*:area:replication" + "client/**:area:client" + "plugin/**:area:pluggable" + "cmake/**:area:build" + "mysql-test/**:area:tests" + "docs/**:area:docs" +) + +safe_ext() { case "$1" in *.cc|*.cpp|*.cxx|*.c|*.h|*.hpp|*.ic) echo "//";; \ + *.cmake|*.sh|*.txt|*.cnf) echo "#";; *.md) echo "<!--md-->";; *) echo "";; esac; } + +touched=0 +for entry in "${globs[@]}"; do + glob="${entry%%:*}"; label="${entry#*:}" + # first tracked file matching the glob that has a comment-safe extension + file="" + while IFS= read -r f; do + pre="$(safe_ext "$f")"; [ -n "$pre" ] && { file="$f"; prefix="$pre"; break; } + done < <(git ls-files -- $glob 2>/dev/null) + if [ -z "$file" ]; then echo "skip $label (no comment-safe file matched $glob)"; continue; fi + if [ "$prefix" = "<!--md-->" ]; then + printf '\n<!-- %s -->\n' "$MARK" >> "$file" + else + printf '\n%s %s\n' "$prefix" "$MARK" >> "$file" + fi + echo "touch $label -> $file" + touched=$((touched+1)) +done + +[ "$touched" -eq 0 ] && { echo "nothing touched"; exit 1; } +git add -A +git commit -q -m "demo: exercise PR auto-labeler across areas (revert before merge)" +echo "committed demo edits ($touched files)." + +if [ "${1:-}" = "--push" ]; then + git push origin HEAD + echo "pushed." +fi + +cat <<'EOF' + +To undo this demo commit after the screenshots: + git revert --no-edit HEAD # keeps history, or + git reset --hard HEAD~1 && git push --force-with-lease +EOF diff --git a/scripts/dev/format.sh b/scripts/dev/format.sh new file mode 100755 index 0000000000..aa5cf6419a --- /dev/null +++ b/scripts/dev/format.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# Format staged C/C++ using the repo's existing .clang-format. +# Run with no args as a pre-commit hook; pass paths to format specific files. +set -euo pipefail +REPO_ROOT="$(git rev-parse --show-toplevel)" +CF="$(command -v clang-format-18 || command -v clang-format)" + +if [ "$#" -gt 0 ]; then files="$*"; +else files="$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(c|cc|cpp|h|hpp)$' || true)"; fi + +[ -z "${files// }" ] && { echo "format: nothing to do"; exit 0; } +for f in $files; do [ -f "$REPO_ROOT/$f" ] && "$CF" -i --style=file "$REPO_ROOT/$f"; done +git add $files 2>/dev/null || true +echo "format: applied .clang-format to changed C/C++ files" diff --git a/scripts/dev/mtr.sh b/scripts/dev/mtr.sh new file mode 100755 index 0000000000..351e40e589 --- /dev/null +++ b/scripts/dev/mtr.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Run MySQL Test Run the same way CI does. Usage: +# mtr.sh smoke curated fast subset (≈ the PR check) +# mtr.sh main full main suite +# mtr.sh --suite=innodb ... raw args passed straight to ./mtr +set -euo pipefail + +REPO_ROOT="$(git rev-parse --show-toplevel)" +BUILD_DIR="${BUILD_DIR:-$REPO_ROOT/build}" +cd "$BUILD_DIR/mysql-test" + +case "${1:-smoke}" in + smoke) + # Fast signal: a representative slice across major areas, parallelized. + exec ./mtr --parallel=auto --force --suite=main \ + --do-test-list="$REPO_ROOT/mysql-test/collections/smoke.list" + ;; + main) + exec ./mtr --parallel=auto --force --suite=main + ;; + *) + exec ./mtr "$@" + ;; +esac