diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 247a550d9f0..00000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,17 +0,0 @@ -environment: - matrix: - - TARGET: x86_64-pc-windows-msvc - - TARGET: i686-pc-windows-msvc - - TARGET: x86_64-pc-windows-gnu - - TARGET: i686-pc-windows-gnu - RUST_BACKTRACE: full -install: - - curl -sSf -o rustup-init.exe https://win.rustup.rs/ - - rustup-init.exe -y --default-host %TARGET% - - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - - rustc -vV - - cargo -vV -build: false -test_script: - - cargo build --verbose --features yaml - - cargo test --verbose --features yaml diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 00000000000..23bf481790c --- /dev/null +++ b/.clippy.toml @@ -0,0 +1 @@ +msrv = "1.60.0" # MSRV diff --git a/.clog.toml b/.clog.toml deleted file mode 100644 index 5a62fa4badf..00000000000 --- a/.clog.toml +++ /dev/null @@ -1,13 +0,0 @@ -[clog] -repository = "https://github.com/kbknapp/clap-rs" -outfile = "CHANGELOG.md" -from-latest-tag = true - -[sections] -Performance = ["perf"] -Improvements = ["impr", "im", "imp"] -Documentation = ["docs"] -Deprecations = ["depr"] -Examples = ["examples"] -"New Settings" = ["setting", "settings"] -"API Additions" = ["add", "api"] diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index ba84eab478d..00000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,115 +0,0 @@ -# How to Contribute - -Contributions are always welcome! And there is a multitude of ways in which you can help depending on what you like to do, or are good at. Anything from documentation, code cleanup, issue completion, new features, you name it, even filing issues is contributing and greatly appreciated! - -Another really great way to help is if you find an interesting, or helpful way in which to use `clap`. You can either add it to the [examples/](examples) directory, or file an issue and tell me. I'm all about giving credit where credit is due :) - -### Testing Code - -To test with all features both enabled and disabled, you can run these commands: - -```sh -$ cargo test --no-default-features -$ cargo test --features "yaml unstable" -``` - -Alternatively, if you have [`just`](https://github.com/casey/just) installed you can run the prebuilt recipes. *Not* using `just` is perfectly fine as well, it simply bundles commands automatically. - -For example, to test the code, as above simply run: - -```sh -$ just run-tests -``` - -From here on, I will list the appropriate `cargo` command as well as the `just` command. - -Sometimes it's helpful to only run a subset of the tests, which can be done via: - -```sh -$ cargo test --test - -# Or - -$ just run-test -``` - -### Linting Code - -During the CI process `clap` runs against many different lints using [`clippy`](https://github.com/Manishearth/rust-clippy). In order to check if these lints pass on your own computer prior to submitting a PR you'll need a nightly compiler. - -In order to check the code for lints run either: - -```sh -$ rustup override add nightly -$ cargo build --features lints -$ rustup override remove - -# Or - -$ just lint -``` - -### Debugging Code - -Another helpful technique is to see the `clap` debug output while developing features. In order to see the debug output while running the full test suite or individual tests, run: - -```sh -$ cargo test --features debug - -# Or for individual tests -$ cargo test --test --features debug - -# The corresponding just command for individual debugging tests is: -$ just debug -``` - -### Commit Messages - -I use a [conventional](https://github.com/ajoslin/conventional-changelog/blob/a5505865ff3dd710cf757f50530e73ef0ca641da/conventions/angular.md) changelog format so I can update my changelog automatically using [clog](https://github.com/clog-tool/clog-cli) - - * Please format your commit subject line using the following format: `TYPE(COMPONENT): MESSAGE` where `TYPE` is one of the following: - - `api` - An addition to the API - - `setting` - A new `AppSettings` variant - - `feat` - A new feature of an existing API - - `imp` - An improvement to an existing feature/API - - `perf` - A performance improvement - - `docs` - Changes to documentation only - - `tests` - Changes to the testing framework or tests only - - `fix` - A bug fix - - `refactor` - Code functionality doesn't change, but underlying structure may - - `style` - Stylistic changes only, no functionality changes - - `wip` - A work in progress commit (Should typically be `git rebase`'ed away) - - `chore` - Catch all or things that have to do with the build system, etc - - `examples` - Changes to existing example, or a new example - * The `COMPONENT` is optional, and may be a single file, directory, or logical component. Parenthesis can be omitted if you are opting not to use the `COMPONENT`. - -### Tests and Documentation - -1. Create tests for your changes -2. **Ensure the tests are passing.** Run the tests (`cargo test --features "yaml unstable"`), alternatively `just run-tests` if you have `just` installed. -3. **Optional** Run the lints (`cargo build --features lints`) (requires a nightly compiler), alternatively `just lint` -4. Ensure your changes contain documentation if adding new APIs or features. - -### Preparing the PR - -1. `git rebase` into concise commits and remove `--fixup`s or `wip` commits (`git rebase -i HEAD~NUM` where `NUM` is number of commits back to start the rebase) -2. Push your changes back to your fork (`git push origin $your-branch`) -3. Create a pull request against `master`! (You can also create the pull request first, and we'll merge when ready. This a good way to discuss proposed changes.) - -### Other ways to contribute - -Another really great way to help is if you find an interesting, or helpful way in which to use `clap`. You can either add it to the [examples/](../examples) directory, or file an issue and tell me. I'm all about giving credit where credit is due :) - -### Goals - -There are a few goals of `clap` that I'd like to maintain throughout contributions. If your proposed changes break, or go against any of these goals we'll discuss the changes further before merging (but will *not* be ignored, all contributes are welcome!). These are by no means hard-and-fast rules, as I'm no expert and break them myself from time to time (even if by mistake or ignorance :P). - -* Remain backwards compatible when possible - - If backwards compatibility *must* be broken, use deprecation warnings if at all possible before removing legacy code - - This does not apply for security concerns -* Parse arguments quickly - - Parsing of arguments shouldn't slow down usage of the main program - - This is also true of generating help and usage information (although *slightly* less stringent, as the program is about to exit) -* Try to be cognizant of memory usage - - Once parsing is complete, the memory footprint of `clap` should be low since the main program is the star of the show -* `panic!` on *developer* error, exit gracefully on *end-user* error diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000000..0e83d98c436 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: clap diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index b10cc73dbd3..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,33 +0,0 @@ - - -### Rust Version - -* Use the output of `rustc -V` - -### Affected Version of clap - -* Can be found in Cargo.lock of your project (i.e. `grep clap Cargo.lock`) - -### Expected Behavior Summary - - -### Actual Behavior Summary - - -### Steps to Reproduce the issue - - -### Sample Code or Link to Sample Code - - -### Debug output - -Compile clap with cargo features `"debug"` such as: - -```toml -[dependencies] -clap = { version = "2", features = ["debug"] } -``` -The output may be very long, so feel free to link to a gist or attach a text file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..61942039653 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,66 @@ +name: Bug report +description: An issue with clap, clap_complete, clap_derive, or clap_mangen +labels: 'C-bug' +body: + - type: checkboxes + attributes: + label: Please complete the following tasks + options: + - label: I have searched the [discussions](https://github.com/clap-rs/clap/discussions) + required: true + - label: I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues + required: true + - type: input + attributes: + label: Rust Version + description: Output of `rustc -V` + validations: + required: true + - type: input + attributes: + label: Clap Version + description: Can be found in Cargo.lock or Cargo.toml of your project (i.e. `grep clap Cargo.lock`). PLEASE DO NOT PUT "latest" HERE, use precise version. Put `master` (or other branch) if you're using the repo directly. + validations: + required: true + - type: textarea + attributes: + label: Minimal reproducible code + description: Please write a minimal complete program which has this bug. Do not point to an existing repository. + value: | + ```rust + fn main() {} + ``` + validations: + required: true + - type: textarea + attributes: + label: Steps to reproduce the bug with the above code + description: A command like `cargo run -- options...` or multiple commands. + validations: + required: true + - type: textarea + attributes: + label: Actual Behaviour + description: When I do like *this*, *that* is happening and I think it shouldn't. + validations: + required: true + - type: textarea + attributes: + label: Expected Behaviour + description: I think *this* should happen instead. + validations: + required: true + - type: textarea + attributes: + label: Additional Context + description: Add any other context about the problem here. + - type: textarea + attributes: + label: Debug Output + description: | + Compile clap with `debug` feature: + + ```toml + [dependencies] + clap = { version = "*", features = ["debug"] } + ``` diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..c528a2ad222 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Ask a question + about: For support or brainstorming + url: https://github.com/clap-rs/clap/discussions/new diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..a28b8a62fe4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,38 @@ +name: Feature request +description: Suggest an idea for this project +labels: 'C-enhancement' +body: + - type: checkboxes + attributes: + label: Please complete the following tasks + options: + - label: I have searched the [discussions](https://github.com/clap-rs/clap/discussions) + required: true + - label: I have searched the [open](https://github.com/clap-rs/clap/issues) and [rejected](https://github.com/clap-rs/clap/issues?q=is%3Aissue+label%3AS-wont-fix+is%3Aclosed) issues + required: true + - type: input + attributes: + label: Clap Version + description: Can be found in Cargo.lock or Cargo.toml of your project (i.e. `grep clap Cargo.lock`). PLEASE DO NOT PUT "latest" HERE, use precise version. Put `master` (or other branch) if you're using the repo directly. + validations: + required: true + - type: textarea + attributes: + label: Describe your use case + description: Describe the problem you're trying to solve. This is not mandatory and we *do* consider features without a specific use case, but real problems have priority. + validations: + required: true + - type: textarea + attributes: + label: Describe the solution you'd like + description: Please explain what the wanted solution should look like. You are **strongly encouraged** to attach a snippet of (pseudo)code. + validations: + required: true + - type: textarea + attributes: + label: Alternatives, if applicable + description: A clear and concise description of any alternative solutions or features you've managed to come up with. + - type: textarea + attributes: + label: Additional Context + description: Add any other context about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..0757758c8eb --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..97ff7438db4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: monthly + time: "07:00" + open-pull-requests-limit: 10 + labels: + - C-dependencies +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: monthly + open-pull-requests-limit: 10 + labels: + - "C-dependencies" diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 00000000000..658c28adc7e --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,130 @@ +# These settings are synced to GitHub by https://probot.github.io/apps/settings/ + +repository: + description: A full featured, fast Command Line Argument Parser for Rust + homepage: docs.rs/clap + topics: rust cli command-line argparse clap + has_issues: true + has_projects: false + has_wiki: false + has_downloads: true + default_branch: master + + # Preference: people do clean commits + allow_merge_commit: true + # Backup in case we need to clean up commits + allow_squash_merge: true + # Not really needed + allow_rebase_merge: false + + # Manual: allow_auto_merge: true, see https://github.com/probot/settings/issues/402 + delete_branch_on_merge: true + +#labels: +# - name: "A-builder" +# description: "Area: Builder API" +# color: '#f7e101' +# - name: "A-derive" +# description: "Area: #[derive]` macro API" +# color: '#f7e101' +# - name: "A-docs" +# description: "Area: documentation, including docs.rs, readme, examples, etc..." +# color: '#f7e101' +# - name: "A-completion" +# description: "Area: completion generator" +# color: '#f7e101' +# - name: "A-help" +# description: "Area: Help or usage messages" +# color: '#f7e101' +# - name: "A-meta" +# description: "Area: administrative question or tracking issue" +# color: '#f7e101' +# - name: "A-parsing" +# description: "Area: Parser's logic and needs it changed somehow." +# color: '#f7e101' +# - name: "A-validators" +# description: "Area: ArgMatches validation logic +# color: '#f7e101' +# - name: "C-bug" +# description: "Category: Things not working as expected" +# color: '#f5f1fd' +# - name: "C-enhancement" +# description: "Category: Raise on the bar on expectations" +# color: '#f5f1fd' +# - name: "C-tracking-issue" +# description: "Category: A tracking issue for an unstable feature" +# color: '#f5f1fd' +# - name: "C-dependencies" +# description: "Category: Updating dependencies" +# color: '#f5f1fd' +# - name: "E-easy" +# description: "Call for participation: Experience needed to fix: Easy / not much" +# color: '#02E10C' +# - name: "E-medium" +# description: "Call for participation: Experience needed to fix: Medium / intermediate" +# color: '#02E10C' +# - name: "E-hard" +# description: "Call for participation: Experience needed to fix: Hard / a lot" +# color: '#02E10C' +# - name: "E-help-wanted" +# description: "Call for participation: Help is requested to fix this issue." +# color: '#02E10C' +# - name: "S-triage" +# description: "Status: New; needs maintainer attention." +# color: '#D3DDDD' +# - name: "S-blocked" +# description: "Status: Blocked on something else such as an RFC or other implementation work." +# color: '#D3DDDD' +# - name: "S-experimental" +# description: "Status: Ongoing experiment that does not require reviewing and won't be merged in its current state." +# color: '#D3DDDD' +# - name: "S-waiting-on-design" +# description: "Status: Waiting on user-facing design to be resolved before implementing" +# color: '#D3DDDD' +# - name: "S-waiting-on-decision" +# description: "Status: Waiting on a go/no-go before implementing" +# color: '#D3DDDD' +# - name: "S-waiting-on-mentor" +# description: "Status: Needs elaboration on the details before doing a 'Call for participation'" +# color: '#D3DDDD' +# - name: "S-waiting-on-author" +# description: "Status: This is awaiting some action (such as code changes or more information) from the author." +# color: '#D3DDDD' +# - name: "M-breaking-change" +# description: "Meta: Implementing or merging this will introduce a breaking change." +# color: '#E10C02' +# - name: "M-unreviewed" +# description: "Meta: Request for post-merge review." +# color: '#E10C02' + +branches: + - name: master + protection: + required_pull_request_reviews: null + required_conversation_resolution: true + required_status_checks: + # Required. Require branches to be up to date before merging. + strict: false + contexts: ["CI", "Lint Commits", "Spell Check with Typos"] + enforce_admins: false + restrictions: null + - name: v2-master + protection: + required_pull_request_reviews: null + required_conversation_resolution: true + required_status_checks: + # Required. Require branches to be up to date before merging. + strict: false + contexts: ["CI", "Lint Commits", "Spell Check with Typos"] + enforce_admins: false + restrictions: null + - name: v3-master + protection: + required_pull_request_reviews: null + required_conversation_resolution: true + required_status_checks: + # Required. Require branches to be up to date before merging. + strict: false + contexts: ["CI", "Lint Commits", "Spell Check with Typos"] + enforce_admins: false + restrictions: null diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 00000000000..d12326fe5ed --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,26 @@ +name: Security audit +on: + pull_request: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' + push: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' + schedule: + - cron: '3 3 3 * *' +permissions: + contents: read +jobs: + security_audit: + permissions: + issues: write # to create issues (actions-rs/audit-check) + checks: write # to create check (actions-rs/audit-check) + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..1a46dfcc9a5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,187 @@ +name: CI +on: + pull_request: + push: + branches: ["*master"] + schedule: + - cron: '3 3 3 * *' +permissions: + contents: read + +jobs: + ci: + permissions: + contents: none + name: CI + needs: [test, check, docs, rustfmt, clippy] + runs-on: ubuntu-latest + steps: + - name: Done + run: exit 0 + test: + name: Test + strategy: + matrix: + build: [linux, windows, mac, minimal, default, next] + include: + - build: linux + os: ubuntu-latest + rust: "stable" + features: "full" + - build: windows + os: windows-latest + rust: "stable" + features: "full" + - build: mac + os: macos-latest + rust: "stable" + features: "full" + - build: minimal + os: ubuntu-latest + rust: "stable" + features: "minimal" + - build: default + os: ubuntu-latest + rust: "stable" + features: "default" + - build: next + os: ubuntu-latest + rust: "stable" + features: "next" + continue-on-error: ${{ matrix.rust != 'stable' }} + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + - uses: Swatinem/rust-cache@v1 + - name: Build + run: make build-${{matrix.features}} + - name: Test + run: make test-${{matrix.features}} + - name: Test (benches) + run: make test-${{matrix.features}} ARGS='--workspace --benches' + - name: Test (ultra-minimal) + if: matrix.build == 'minimal' + run: make test-minimal ARGS='--manifest-path Cargo.toml' + check: + name: Check + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + build: [msrv, wasm, wasm-wasi, debug, release] + include: + - build: msrv + rust: 1.60.0 # MSRV + target: x86_64-unknown-linux-gnu + features: full + - build: wasm + rust: stable + target: wasm32-unknown-unknown + features: wasm + - build: wasm-wasi + rust: stable + target: wasm32-wasi + features: wasm + - build: debug + rust: stable + target: x86_64-unknown-linux-gnu + features: debug + - build: release + rust: stable + target: x86_64-unknown-linux-gnu + features: release + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + - uses: Swatinem/rust-cache@v1 + - name: Check + run: make check-${{ matrix.features }} + ui: + name: UI Tests + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + features: [default, next] + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.60.0 # MSRV + profile: minimal + override: true + - uses: Swatinem/rust-cache@v1 + - name: UI Tests + run: make test-ui-${{ matrix.features }} + docs: + name: Docs + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.60.0 # MSRV + profile: minimal + override: true + - uses: Swatinem/rust-cache@v1 + - name: Check documentation + env: + RUSTDOCFLAGS: -D warnings + run: make doc + rustfmt: + name: rustfmt + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + # Not MSRV because its harder to jump between versions and people are + # more likely to have stable + toolchain: stable + profile: minimal + override: true + components: rustfmt + - uses: Swatinem/rust-cache@v1 + - name: Check formatting + run: cargo fmt --all -- --check + clippy: + name: clippy + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: 1.60.0 # MSRV + profile: minimal + override: true + components: clippy + - uses: Swatinem/rust-cache@v1 + - name: Lint (ultra-minimal) + run: make clippy-minimal ARGS='--manifest-path Cargo.toml' + - name: Lint (minimal) + run: make clippy-minimal + - name: Lint (all) + run: make clippy-full + - name: Lint (release) + run: make clippy-release diff --git a/.github/workflows/committed.yml b/.github/workflows/committed.yml new file mode 100644 index 00000000000..5d2f2970ddf --- /dev/null +++ b/.github/workflows/committed.yml @@ -0,0 +1,16 @@ +# Not run as part of pre-commit checks because they don't handle sending the correct commit +# range to `committed` +name: Lint Commits +on: [pull_request] + +jobs: + committed: + name: Lint Commits + runs-on: ubuntu-latest + steps: + - name: Checkout Actions Repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Lint Commits + uses: crate-ci/committed@master diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml new file mode 100644 index 00000000000..f4c23f1a449 --- /dev/null +++ b/.github/workflows/post-release.yml @@ -0,0 +1,42 @@ +name: post-release +on: + push: + tags: + - "v*" +permissions: + contents: read + +jobs: + create-release: + permissions: + contents: write # for actions/create-release to create a release + name: create-release + runs-on: ubuntu-latest + outputs: + upload_url: ${{ steps.release.outputs.upload_url }} + release_version: ${{ env.RELEASE_VERSION }} + steps: + - name: Get the release version from the tag + shell: bash + if: env.RELEASE_VERSION == '' + run: | + # See: https://github.amrom.workers.devmunity/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027 + echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + echo "version is: ${{ env.RELEASE_VERSION }}" + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 1 + - name: Generate Release Notes + run: | + ./.github/workflows/release-notes.py --tag ${{ env.RELEASE_VERSION }} --output notes-${{ env.RELEASE_VERSION }}.md + cat notes-${{ env.RELEASE_VERSION }}.md + - name: Create GitHub release + id: release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ env.RELEASE_VERSION }} + release_name: ${{ env.RELEASE_VERSION }} + body_path: notes-${{ env.RELEASE_VERSION }}.md diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 00000000000..05c39619f62 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,15 @@ +name: pre-commit +on: + pull_request: + push: + branches: ["*master"] +permissions: {} # none +jobs: + pre-commit: + permissions: + contents: read + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/release-notes.py b/.github/workflows/release-notes.py new file mode 100755 index 00000000000..7a0d26d23d2 --- /dev/null +++ b/.github/workflows/release-notes.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import argparse +import re +import pathlib +import sys + + +_STDIO = pathlib.Path("-") + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-i", "--input", type=pathlib.Path, default="CHANGELOG.md") + parser.add_argument("--tag", required=True) + parser.add_argument("-o", "--output", type=pathlib.Path, required=True) + args = parser.parse_args() + + if args.input == _STDIO: + lines = sys.stdin.readlines() + else: + with args.input.open() as fh: + lines = fh.readlines() + version = args.tag.lstrip("v") + + note_lines = [] + for line in lines: + if line.startswith("## ") and version in line: + note_lines.append(line) + elif note_lines and line.startswith("## "): + break + elif note_lines: + note_lines.append(line) + + notes = "".join(note_lines).strip() + if args.output == _STDIO: + print(notes) + else: + args.output.write_text(notes) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/rust-next.yml b/.github/workflows/rust-next.yml new file mode 100644 index 00000000000..dac1e4e27c6 --- /dev/null +++ b/.github/workflows/rust-next.yml @@ -0,0 +1,117 @@ +name: rust-next +on: + schedule: + - cron: '3 3 3 * *' +permissions: + contents: read + +jobs: + test: + name: Test + strategy: + matrix: + build: [stable, linux, windows, mac, nightly, minimal, default, next] + include: + - build: stable + os: ubuntu-latest + rust: "stable" + features: "full" + - build: linux + os: ubuntu-latest + rust: "beta" + features: "full" + - build: windows + os: windows-latest + rust: "beta" + features: "full" + - build: mac + os: macos-latest + rust: "beta" + features: "full" + - build: nightly + os: ubuntu-latest + rust: "nightly" + features: "full" + - build: minimal + os: ubuntu-latest + rust: "stable" + features: "minimal" + - build: default + os: ubuntu-latest + rust: "stable" + features: "default" + - build: next + os: ubuntu-latest + rust: "stable" + features: "next" + continue-on-error: ${{ matrix.rust != 'stable' }} + runs-on: ${{ matrix.os }} + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + - uses: Swatinem/rust-cache@v1 + - name: Build + run: make build-${{matrix.features}} + - name: Test + run: make test-${{matrix.features}} + - name: Test (benches) + run: make test-${{matrix.features}} ARGS='--workspace --benches' + - name: Test (ultra-minimal) + if: matrix.build == 'minimal' + run: make test-minimal ARGS='--manifest-path Cargo.toml' + rustfmt: + name: rustfmt + strategy: + matrix: + rust: + - stable + - beta + continue-on-error: ${{ matrix.rust != 'stable' }} + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + components: rustfmt + - uses: Swatinem/rust-cache@v1 + - name: Check formatting + run: cargo fmt --all -- --check + clippy: + name: clippy + strategy: + matrix: + rust: + - 1.60.0 # MSRV + - stable + continue-on-error: ${{ matrix.rust != '1.60.0' }} # MSRV + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + components: clippy + - uses: Swatinem/rust-cache@v1 + - name: Lint (ultra-minimal) + run: make clippy-minimal ARGS='--manifest-path Cargo.toml' + - name: Lint Minimal + run: make clippy-minimal + - name: Lint All + run: make clippy-full + - name: Lint (release) + run: make clippy-release diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml new file mode 100644 index 00000000000..c00987ea985 --- /dev/null +++ b/.github/workflows/spelling.yml @@ -0,0 +1,15 @@ +name: Spelling +on: [pull_request] + +permissions: + contents: read + +jobs: + spelling: + name: Spell Check with Typos + runs-on: ubuntu-latest + steps: + - name: Checkout Actions Repository + uses: actions/checkout@v3 + - name: Spell Check Repo + uses: crate-ci/typos@master diff --git a/.gitignore b/.gitignore index 34253e90833..eb5a316cbd1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1 @@ -# Compiled files -*.o -*.so -*.rlib -*.dll - -# Executables -*.exe - -# Generated by Cargo -/target/ -/clap-test/target/ - -# Cargo files -Cargo.lock - -# Temp files -.*~ - -# Backup files -*.bak -*.bk -*.orig - -# Project files -.vscode/* -.idea/* +target diff --git a/.mention-bot b/.mention-bot deleted file mode 100644 index f339f59cd0f..00000000000 --- a/.mention-bot +++ /dev/null @@ -1,9 +0,0 @@ -{ - "findPotentialReviewers": false, - "alwaysNotifyForPaths": [ - { - "name": "kbknapp", - "files": ["**/*.rs", "**/*.md", "*"] - } - ] -} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000000..79df3909b13 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + stages: [commit] + - id: check-json + stages: [commit] + - id: check-toml + stages: [commit] + - id: check-merge-conflict + stages: [commit] + - id: check-case-conflict + stages: [commit] + - id: detect-private-key + stages: [commit] + - repo: https://github.com/crate-ci/committed + rev: v1.0.1 + hooks: + - id: committed + stages: [commit-msg] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1e3cfea70ea..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,58 +0,0 @@ -sudo: true -language: rust -cache: cargo -rust: - - nightly - - nightly-2017-10-11 - - beta - - stable - - 1.20.0 -matrix: - allow_failures: - - rust: nightly -before_script: - - | - pip install git+git://github.com/kbknapp/travis-cargo.git --user && - export PATH=$HOME/.local/bin:$PATH - - | - if [[ "$TRAVIS_RUST_VERSION" == "1.13.0" ]]; then - echo "Old Rust detected, removing version-sync dependency" - sed -i "/^version-sync =/d" Cargo.toml - rm "tests/version-numbers.rs" - fi -script: - - | - travis-cargo test -- --verbose --no-default-features && - travis-cargo --skip nightly test -- --verbose --features "yaml unstable" && - travis-cargo --only nightly test -- --verbose --features "yaml unstable nightly" && - travis-cargo --only nightly bench -addons: - apt: - packages: - - libcurl4-openssl-dev - - libelf-dev - - libdw-dev - - cmake - - gcc - - binutils-dev -after_success: - - | - wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && - tar xzf master.tar.gz && - cd kcov-master && - mkdir build && - cd build && - cmake .. && - make && - sudo make install && - cd ../.. && - rm -rf kcov-master && - cargo clean && - cargo test --no-run --features "yaml unstable" && - for file in target/debug/*-*; do mkdir -p "target/cov/$(basename $file)"; kcov --exclude-pattern=/.cargo --verify "target/cov/$(basename $file)" "$file"; done && - kcov --coveralls-id=$TRAVIS_JOB_ID --merge target/cov target/cov/* && - echo "Uploaded code coverage" -env: - global: - - TRAVIS_CARGO_NIGHTLY_FEATURE=lints - - secure: JLBlgHY6OEmhJ8woewNJHmuBokTNUv7/WvLkJGV8xk0t6bXBwSU0jNloXwlH7FiQTc4TccX0PumPDD4MrMgxIAVFPmmmlQOCmdpYP4tqZJ8xo189E5zk8lKF5OyaVYCs5SMmFC3cxCsKjfwGIexNu3ck5Uhwe9jI0tqgkgM3URA= diff --git a/CHANGELOG.md b/CHANGELOG.md index 844dc5b8cd3..9f7c3e31194 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,81 +1,1573 @@ - -### v2.29.2 (2018-01-16) +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + + +## [Unreleased] - ReleaseDate + +## [4.0.26] - 2022-11-16 + +### Fixes + +- *(error)* Fix typos in `ContextKind::as_str` + +## [4.0.25] - 2022-11-15 + +### Features + +- *(error)* Report available subcommands when required subcommand is missing + +## [4.0.24] - 2022-11-14 + +### Fixes + +- Avoid panic when printing an argument that isn't built + +## [4.0.23] - 2022-11-11 + +### Fixes + +- Don't panic on reporting invalid-long errors when followed by invalid UTF8 +- *(help)* Clarified argument to `help` subcommand + +## [4.0.22] - 2022-11-07 + +### Fixes + +- *(help)* Don't overflow into next-line-help early due to stale (pre-v4) padding calculations + +## [4.0.21] - 2022-11-07 + +### Features + +- *(derive)* `long_about` and `long_help` attributes, without a value, force using doc comment (before it wouldn't be set if there wasn't anything different than the short help) + +## [4.0.20] - 2022-11-07 + +### Fixes + +- *(derive)* Allow defaulted value parser for '()' fields + +## [4.0.19] - 2022-11-04 + +### Features + +- `ColorChoice` now implements `ValueEnum` + +## [4.0.18] - 2022-10-20 + +### Fixes + +- *(derive)* Allow `#[command(skip)]` to also work with enum variants with a value + +## [4.0.17] - 2022-10-18 + +### Fixes + +- Allow using `Arg::last(true)` with `Arg::value_hint(ValueHint::CommandWithArguments)` + +## [4.0.16] - 2022-10-18 + +### Fixes + +- `Arg::exclusive(true)` should not be exclusive with the argument's own `ArgGroup` + +## [4.0.15] - 2022-10-13 + +### Fixes + +- *(error)* Don't suggest `--` when it doesn't help +- *(error)* Be more consistent in quoting, punctuation, and indentation in errors + +## [4.0.14] - 2022-10-12 + +### Fixes + +- Only put `ArgGroup` in `ArgMatches` when explicitly specified, fixing derives handling of option-flattened fields (#4375) + +## [4.0.13] - 2022-10-11 + +### Features + +- *(derive)* Allow `()` for fields to mean "don't read" (#4371) + +## [4.0.12] - 2022-10-10 + +### Features + +- Added `TypedValueParser::try_map` for when adapting an existing `TypedValueParser` can fail +- *(error)* Create errors like clap with `Error::new`, `Error::with_cmd`, and `Error::insert` + +## [4.0.11] - 2022-10-09 + +### Fixes + +- *(help)* Fix wrapping calculations with ANSI escape codes + +## [4.0.10] - 2022-10-05 + +### Features + +- *(derive)* Support `#[arg(flatten)]` on `Option` types (#4211, #4350) + +## [4.0.9] - 2022-10-03 + +### Fixes + +- *(derive)* Process doc comments for `#[command(subcommand)]` like in clap v3 + +## [4.0.8] - 2022-10-01 + +### Fixes + +- *(derive)* Remove a low-value assert preventing defaulting `Help` and `Version` actions + +## [4.0.7] - 2022-09-30 + +### Features + +- *(derive)* Populate implicit ArgGroup (#3165) + +### Fixes + +- *(derive)* Support `#[group(skip)]` on `Parser` derive +- *(derive)* Tell users about implicit arg groups when running into group name conflicts +- *(error)* Don't report unrelated groups in conflict or requires errors + +## [4.0.6] - 2022-09-30 + +### Features + +- *(derive)* Support `#[group(skip)]` (#4279, #4301) + +## [4.0.5] - 2022-09-30 + +## [4.0.4] - 2022-09-29 + +### Fixes + +- *(error)* Specialize the self-conflict error to look like clap v3 + +## [4.0.3] - 2022-09-29 + +### Fixes + +- *(error)* Quote literals consistently +- *(error)* Stylize escape (`--`) suggestions +- *(error)* Format help flag as a literal + +## [4.0.2] - 2022-09-28 + +### Fixes + +- *(parser)* `SetFalse` should conflict with itself like `SetTrue` and `Set` +- *(parser)* Allow one-off overrides + +## [4.0.1] - 2022-09-28 + +### Fixes + +- *(derive)* Ensure `#[clap(...)]` attribute still works + +## [4.0.0] - 2022-09-28 + +### Highlights + +**`Arg::num_args(range)`** + +Clap has had several ways for controlling how many values will be captured without always being clear on how they interacted, including +- `Arg::multiple_values(true)` +- `Arg::number_of_values(4)` +- `Arg::min_values(2)` +- `Arg::max_values(20)` +- `Arg::takes_value(true)` + +These have now all been collapsed into `Arg::num_args` which accepts both +single values and ranges of values. `num_args` controls how many raw arguments +on the command line will be captured as values per occurrence and independent +of value delimiters. + +See [Issue 2688](https://github.com/clap-rs/clap/issues/2688) for more background. + +**Polishing Help** + +Clap strives to give a polished CLI experience out of the box with little +ceremony. With some feedback that has accumulated over time, we took this +release as an opportunity to re-evaluate our `--help` output to make sure it is +meeting that goal. + +In doing this evaluation, we wanted to keep in mind: +- Whether other CLIs had ideas that make sense to apply +- Providing an experience that fits within the rest of applications and works across all shells + +Before: +``` +git +A fictional versioning CLI + +USAGE: + git + +OPTIONS: + -h, --help Print help information + +SUBCOMMANDS: + add adds things + clone Clones repos + help Print this message or the help of the given subcommand(s) + push pushes things + stash +``` + +After: +``` +A fictional versioning CLI + +Usage: git + +Commands: + clone Clones repos + push pushes things + add adds things + stash + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help information +``` +- name/version header was removed because we couldn't justify the space it occupied when + - Usage already includes the name + - `--version` is available for showing the same thing (if the program has a version set) +- Usage was dropped to one line to save space +- Focus is put on the subcommands +- Headings are now Title case +- The more general term "command" is used rather than being explicit about being "subcommands" +- The output is more dense with the expectation that it won't affect legibility but will allow more content +- We've moved to a more neutral palette for highlighting elements (not highlighted above) + +In talking to users, we found some that liked clap's `man`-like experience. +When deviating from this, we are making the assumption that those are more +power users and that the majority of users wouldn't look as favorably on being +consistent with `man`. + +See [Issue 4132](https://github.com/clap-rs/clap/issues/4132) for more background. + +**More Dynamicism** + +Clap's API has focused on `&str` for performance but this can make +dealing with owned data difficult, like `#[arg(default_value_t)]` generating a +String from the default value. + +Additionally, to avoid `ArgMatches` from borrowing (and for some features we +decided to forgo), clap took the `&str` argument IDs and hashed them. This +prevented us from providing a usable API for iterating over existing arguments. + +Now clap has switched to a string newtype that gives us the flexibility to +decide whether to use `&'static str`, `Cow<'static, str>` for fast dynamic behavior, or +`Box` for dynamic behavior with small binary size. + +As an extension of that work, you can now call `ArgMatches::ids` to iterate +over the arguments and groups that were found when parsing. The newtype `Id` +was used to prevent some classes of bugs and to make it easier to understand +when opaque Ids are used vs user-visible strings. + +**Clearing Out Deprecations** + +Instead of doing all development on clap 4.0.0, we implemented a lot of new features during clap 3's development, deprecating the old API while introducing the new API, including: +- Replacing the implicit behavior for args when parsing them with `ArgAction` +- Replacing various one-off forms of value validation with the `ValueParser` API + - Allowing derives to automatically do the right thing for `PathBuf` (allowing invalid UTF-8) +- Replacing `AppSettings` and `ArgSettings` enums with getters/setters +- Clarifying terms and making them more consistent + +### Migrating + +Steps: + +0. [Upgrade to v3](https://github.com/clap-rs/clap/blob/v3-master/CHANGELOG.md#migrating) if you haven't already +1. Add CLI tests (including example below), `-h` and `--help` output at a minimum (recommendation: [trycmd](https://docs.rs/trycmd/) for snapshot testing) +2. *If using Builder API*: Explicitly set the `arg.action(ArgAction::...)` on each argument (`StoreValue` for options and `IncOccurrences` for flags) +3. Run `cargo check --features clap/deprecated` and resolve all deprecation warnings +4. Upgrade to v4 +5. Update feature flags + - *If `default-features = false`*, run `cargo add clap -F help,usage,error-context` + - Run `cargo add clap -F wrap_help` unless you want to hard code line wraps +6. Resolve compiler errors +7. Resolve behavior changes (see "subtle changes" under BREAKING CHANGES) +8. *At your leisure:* resolve new deprecation notices + +Example test (derive): +```rust +#[derive(clap::Parser)] +struct Cli { + ... +} + +#[test] +fn verify_cli() { + use clap::CommandFactory; + Cli::command().debug_assert() +} +``` + +Example test (builder): +```rust +fn cli() -> clap::Command { + ... +} + +#[test] +fn verify_cli() { + cli().debug_assert(); +} +``` + +Note: the idiomatic / recommended way of specifying different types of args in the Builder API has changed: + +Before +```rust +.arg(Arg::new("flag").long("flag")) # --flag +.arg(Arg::new("option").long("option").takes_value(true)) # --option