diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ce10944..cbd0adef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: os: [ubuntu, windows, macos] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions-rust-lang/setup-rust-toolchain@v1 - run: cargo test --locked @@ -48,21 +48,21 @@ jobs: os: [ubuntu, windows, macos] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions-rust-lang/setup-rust-toolchain@v1 - - run: cargo clippy + - run: cargo clippy --all-features --all-targets --locked - run: cargo doc --no-deps rust-formatting: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # We don't need any cache here, so use dtolnay action directly # The version of toolchain here should track the latest stable # version of Rust. The version of `cargo fmt` we use here doesn't # influence the version of Rust that is used when `cargo-marker` is built. - - uses: dtolnay/rust-toolchain@1.71.1 + - uses: dtolnay/rust-toolchain@1.72.0 - run: cargo fmt --check # This job ensures required packages can be built with a stable toolchain @@ -75,7 +75,7 @@ jobs: os: [ubuntu, windows, macos] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: rm rust-toolchain.toml - uses: actions-rust-lang/setup-rust-toolchain@v1 with: @@ -100,7 +100,7 @@ jobs: rust-unused-dependencies: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: scripts/download/cargo-machete.sh - run: cargo-machete @@ -108,7 +108,7 @@ jobs: toml: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: scripts/download/taplo.sh - run: taplo fmt --check @@ -116,7 +116,7 @@ jobs: typos: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: scripts/download/typos.sh - run: typos @@ -124,6 +124,21 @@ jobs: mdbook: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: scripts/download/mdbook.sh - run: mdbook build docs/book + + bash-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # We need just the cargo executable to update the `Cargo.lock` file. + # Github runners have `cargo` installed by default, so we don't even + # need to download it. However, we need to remove the `rust-toolchain.toml` + # so that `cargo` rustup proxy doesn't attempt to install the toolchain + # pinned in this repo. + - run: rm rust-toolchain.toml + + # Check that the release automation works as expected + - run: scripts/release/test.sh diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index bffceaf4..5001373d 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'rust-marker/marker' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: ./scripts/download/mdbook.sh - name: Setup deploy directory run: | diff --git a/.github/workflows/release-on-tag.yml b/.github/workflows/release-on-tag.yml new file mode 100644 index 00000000..c6726d1d --- /dev/null +++ b/.github/workflows/release-on-tag.yml @@ -0,0 +1,184 @@ +# This workflow is subscribed only to the `tag` events, and it assumes that +# the version bumps were already done in the `release` workflow. +# See the docs/internal/release.md for more details. + +name: release-on-tag +on: + push: + tags: ['v*'] + +defaults: + run: + shell: bash + +# Contrary to intuition, "contents" covers not only the repository commits +# but also GitHub releases. +permissions: + contents: write + +# It's technically possible to run several releases in parallel if they are +# a regular release and a hotfix from a different branch, but let's try not +# to do that for our own sanity (πŸ¦ΆπŸ”«). +concurrency: + group: ${{ github.workflow }}/${{ github.ref_name }} + cancel-in-progress: true + +jobs: + github-release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/create-gh-release-action@v1 + with: + changelog: CHANGELOG.md + token: ${{ github.token }} + + build: + needs: [github-release] + # GitHub doesn't let you split the `${{ }}` syntax into multiple lines 🀦. + # Anyway, let's return to the topic... The reason why we pin the specific + # versions of OSes is described in the docs under `docs/internal/release.md` + runs-on: ${{ contains(matrix.target, 'windows') && 'windows-2019' || contains(matrix.target, 'darwin') && 'macos-11' || 'ubuntu-20.04' }} + + strategy: + # If it happens so that some binaries fail to build, then we can have a + # release anyway, but just without those binaries that failed to build. + # A build failure, of course would be a very rare event and would be bad, + # but it won't be the end of the world. We can rerun the CI job to try again + # if it was something flaky. GitHub allows rerunning only the failed jobs. + # + # If the failure was something deterministic and specific to the platform, + # then a new hotfix patch should be created for this. + fail-fast: false + matrix: + include: + - { bin: cargo-marker, target: x86_64-pc-windows-msvc } + - { bin: cargo-marker, target: x86_64-apple-darwin } + - { bin: cargo-marker, target: x86_64-unknown-linux-gnu } + - { bin: cargo-marker, target: x86_64-unknown-linux-musl } + + # Who could know that cross-compiling for ARM on windows and macos "just works"?! + # I hope one day it will just work for Linux too, but that's really surprising to + # see how windows and macos beat linux in this regard πŸ‘€. + # + # We use `cargo-zigbuild` instead of the default `cross` build tool because the latter + # is more heavyweight as it depends on `docker`, but `cargo-zigbuild` doesn't. + - { bin: cargo-marker, target: aarch64-pc-windows-msvc } + - { bin: cargo-marker, target: aarch64-apple-darwin } + - { bin: cargo-marker, target: aarch64-unknown-linux-gnu, build_tool: cargo-zigbuild } + - { bin: cargo-marker, target: aarch64-unknown-linux-musl, build_tool: cargo-zigbuild } + + # Unfortunatelly, the driver depends on the dynamic libraries `rustc_driver` and `LLVM`. + # It means we can't have a static `musl` binary for it. It also significantly complicates + # the build for the ARM targets, so in this matrix we have only x86_64 targets. The ARM + # build is handled in a separate job. + - { bin: marker_rustc_driver, target: x86_64-pc-windows-msvc } + - { bin: marker_rustc_driver, target: x86_64-apple-darwin } + - { bin: marker_rustc_driver, target: x86_64-unknown-linux-gnu } + env: + MARKER_ALLOW_DRIVER_BUILD: 1 + + steps: + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + # Release is a rare event. We don't need any caching for it, + # and even if we did use the cache it would already be evicted + # at the time we run the next release because GitHub clears the + # cache after 7 days of inactivity. + cache: false + target: ${{ matrix.target }} + + - uses: taiki-e/upload-rust-binary-action@v1 + with: + bin: ${{ matrix.bin }} + target: ${{ matrix.target }} + token: ${{ github.token }} + checksum: sha256 + tar: all + zip: all + build_tool: ${{ matrix.build_tool || 'cargo' }} + + # This job is the result of desperation. Unfortunatelly we can't easily cross-compile + # the driver for ARM targets, because we need to have the `rustc_private` DLLs to + # be compiled for ARM, but their proc macros need to be compiled for the host x86_64 + # architecture. However, this isn't the case today, and those proc macros are distributed + # pre-compiled only for the architecture of the target machine by `rustup`. + # + # So this job uses docker + QEMU to emulate an ARM machine and build the driver there. + # This, however, is a very slow process, and it's not possible to use this approach + # to build the driver for windows and macos that easily. I have high confidence it's + # possible to hack something for windows, but given the Apple's closed ecosystem... + # + # I don't think it's reasonably possible to compile for macos ARM. They really want + # you to buy their hardware. There is a gossip that GitHub will provide Apple M1 managed + # runners somewhere in the observable future, but until that happens the only option + # to compile for macos on ARM is to buy an Apple M1 machine and make a self-hosted runner, + # but that is ridiculously expensive. + # https://github.com/actions/runner/issues/805#issuecomment-1438149537 + build-driver-on-arm: + needs: [github-release] + runs-on: ubuntu-20.04 + env: + artifact: marker_rustc_driver-aarch64-unknown-linux-gnu + + steps: + - uses: actions/checkout@v4 + + # Concatenate dependent bash files. Unfortunately, the install script + # runs without the repository mounted, so we have to do this to not + # use anything from the repo. + - run: | + cat <> $GITHUB_ENV + install_script<> $GITHUB_ENV + - name: Resolve the next dev version + run: echo "next_dev_version=$(scripts/release/get-next-dev-version.sh ${{ env.release_version }})" >> $GITHUB_ENV + + # To be able to create a commit we need some committer identity. + # Kudos to this guy for sharing how to set the Github Actions bot identity: + # https://github.com/actions/checkout/issues/13#issuecomment-724415212 + - run: | + git config user.name "Github Actions" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + # Create a release commit and tag + - run: scripts/release/set-version.sh ${{ env.release_version }} + --commit "πŸš€ Release v${{ env.release_version }}" + + - run: git tag v${{ env.release_version }} + + # Create a next dev version commit + - run: scripts/release/set-version.sh ${{ env.next_dev_version }} + --commit "🚧 Development v${{ env.next_dev_version }}" + + # Push the branch and the new tag to the remote + - run: git push --atomic origin ${{ github.ref_name }} v${{ env.release_version }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..7f2ca268 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,95 @@ +[Unreleased]: https://github.com/rust-marker/marker/releases/tag/v0.2.1...HEAD +[0.2.1]: https://github.com/rust-marker/marker/releases/tag/v0.2.1 +[0.1.1]: https://github.com/rust-marker/marker/releases/tag/v0.1.1 + +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +[#231]: https://github.com/rust-marker/marker/pull/231 +[#232]: https://github.com/rust-marker/marker/pull/232 +[#239]: https://github.com/rust-marker/marker/pull/239 + +### Added +- [#232]: Add scope config for visitors and `for_each_expr` to `marker_utils` +- [#239]: GitHub releases now provide precompiled binaries of `cargo-marker` and `marker_rustc_driver`. +- [#231]: Significantly improved error handling + +### Internal + +- [#239]: The release flow was automated. It's now a process of updating the `CHANGELOG.md` and doing several clicks to trigger the CI job. + +## [0.2.1] - 2023-08-24 + +See the v0.2.0 milestone for a full list of all changes + +### Added +- Support `.await` and `async` expressions +- Started [The Marker Book](https://rust-marker.github.io/marker/book/) + +### Fixed + +- `cargo marker` now works with lower toolchain versions +- Fixed errors due to drifts in the used toolchain +- Fixed the question mark operator resugar +- `Span`s now properly represent macro expansions + +### Changed +- **BREAKING:** `FnItem<'ast>` and `ClosureExpr<'ast>` no longer implement `CallableData` +- **BREAKING:** Some `Span` methods have been renamed + + +## [0.1.1] - 2023-07-17 + +[#174]: https://github.com/rust-marker/marker/issues/174 + +Marker's first release intended for user testing. + +### Features +This version is far from done, but it already includes most critical AST nodes required for linting. This is an incomplete list of supported elements: + +- Items +- Generics +- Statements +- Expressions +- Patterns +- Types +Marker should be able to handle all stable features, except `async` and `await` expressions. (See: [#174]) + +### Installation +You can install Marker with cargo, like this: + +```bash +cargo install cargo_marker + +# Automatically setup the toolchain and driver +cargo marker setup --auto-install-toolchain +``` + +To run Marker simply specify a lint crate in your `Cargo.toml` file, like this: + +```toml +[workspace.metadata.marker.lints] +marker_lints = "0.1.0" +``` + +And run: + +``` +cargo marker +``` + +This should give you the usual `Checking ...` output of Cargo. + +### Development +You might want to check out Marker's [lint crate template](https://github.com/rust-marker/lint-crate-template) to test the API yourself. + +### Feedback +This release is intended to collect feedback of any kind. If you encounter any bugs, have any thoughts or suggestions, please create an issue or reach out to me directly. + +Happy linting everyone! πŸ¦€ πŸ–ŠοΈ πŸŽ‰ diff --git a/Cargo.toml b/Cargo.toml index 24b9d3bf..fd8b131a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,12 @@ members = [ ] resolver = "2" +[profile.release] +codegen-units = 1 +lto = true +strip = false + + [workspace.package] edition = "2021" keywords = ["marker", "lint"] @@ -18,6 +24,9 @@ license = "MIT OR Apache-2.0" repository = "https://github.com/rust-marker/marker" version = "0.3.0-dev" +# The MSRV is applied to the public library crates published to crates.io +rust-version = "1.66" + [workspace.dependencies] marker_adapter = { path = "./marker_adapter", version = "0.3.0-dev" } marker_api = { path = "./marker_api", version = "0.3.0-dev" } diff --git a/cargo-marker/src/error.rs b/cargo-marker/src/error.rs index b5723451..c7370e9d 100644 --- a/cargo-marker/src/error.rs +++ b/cargo-marker/src/error.rs @@ -71,7 +71,7 @@ marker_lints = { git = "https://github.com/rust-marker/marker" } # An external crate from a registry marker_lints = "0.2.1""# ), - cli_example = display::cli(r#"cargo marker --lints "marker_lints = ''""#), + cli_example = display::cli(r#"cargo marker --lints "marker_lints = '0.2.1'""#), lints = "--lints".blue(), ) } diff --git a/cargo-marker/src/utils/utf8.rs b/cargo-marker/src/utils/utf8.rs index c6f16266..45c1e2bc 100644 --- a/cargo-marker/src/utils/utf8.rs +++ b/cargo-marker/src/utils/utf8.rs @@ -45,7 +45,7 @@ mod tests { use super::*; use expect_test::{expect, Expect}; - fn assert_into_utf8(actual: T, expect: Expect) + fn assert_into_utf8(actual: T, expect: &Expect) where T: IntoUtf8, T::Output: Display, @@ -60,14 +60,14 @@ mod tests { #[test] fn test_into_utf8_success() { - assert_into_utf8(vec![97, 98, 99u8], expect!["abc"]); + assert_into_utf8(vec![97, 98, 99u8], &expect!["abc"]); } #[test] fn test_into_utf8_fail() { assert_into_utf8( vec![97, 98, 255u8], - expect![[r#" + &expect![[r#" Error: Failed to convert to UTF-8 encoded string (dumped it on the line bellow): --- abοΏ½ @@ -77,6 +77,6 @@ mod tests { #[test] fn test_pathbuf_into_utf8_success() { - assert_into_utf8(PathBuf::from("/My/Custom/Path"), expect!["/My/Custom/Path"]); + assert_into_utf8(PathBuf::from("/My/Custom/Path"), &expect!["/My/Custom/Path"]); } } diff --git a/docs/internal/release.md b/docs/internal/release.md new file mode 100644 index 00000000..471f7b0a --- /dev/null +++ b/docs/internal/release.md @@ -0,0 +1,111 @@ +This document describes the release processes and artifacts in `marker`. + +# Artifacts + +As a result of the engineering effort invested into `marker` we get the following items. Each of them comprises the public API of this project. + +## Source packages in `crates.io` + +There are library packages published to crates.io such as `marker_api`, `marker_uitest`. They are intended to be statically linked to the users' lint crates. + +There are binary packages published to crates.io such as `cargo_marker`, `marker_rustc_driver`. They are intended to be used only in exceptional cases to let users build binaries from source. For example, if `marker` lacks support for some platform where users want to utilize it, then they may leverage `cargo install` to compile `marker`'s component binaries from source. However, 99% of the time users will want to download a precompiled binary. + +## Pre-compiled binaries published in a GitHub release + +These are the archived binaries such as `cargo-marker-x86_64-unknown-linux-gnu.tar.gz`. These binaries cover as much platforms as possible meaning different combinations of OS (`windows`, `linux`, `macos`), arch (`x86_64`, `aarch64`), libc implementations (`gnu`, `musl`) and their different versions. + +Let's review what artifacts there are for a single platform. Take this list as an example. + +- `cargo-marker-aarch64-unknown-linux-gnu.tar.gz` +- `cargo-marker-aarch64-unknown-linux-gnu.zip` +- `cargo-marker-aarch64-unknown-linux-gnu.sha256` + +The `tar.gz` and `zip` archives contain a single `cargo-marker` binary file in them. It doesn't matter what archive the user downloads. They have the choice to select either of them. It is usually common to have only `tar.gz` format for `unix`-like systems, and `zip` for `windows`, but we provide both for the reason described below. + +For the cross-platform code it is much more convenient when the same archive format is available for every platform consistently. This means, for example, `cargo-marker` may depend only on the `zip` crate exclusively and expect that `zip` of the `marker_rustc_driver` is always available for download on all platforms. + +> Here is a case of [`terraform`'s](https://developer.hashicorp.com/terraform/downloads) binaries. They are distributed in `zip` for all platforms. + +GitHub is very generous with the releases storage limits. They are [almost unlimited](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases#storage-and-bandwidth-quotas). They difinitely want your project to be open-source πŸ˜‰. + +The `sha256` sum is a small file that users may optionally download together with the archive itself to verify the integrity of the archive. It serves as a signature of the artifact to make sure it was downloaded as expected bit-by-bit with what was published effectively detecting corruptions during the download and making it harder to forge the artifact for the malicious actor. + +```bash +file_stem=cargo-marker-x86_64-unknown-linux-gnu + +# Download two files using one TCP connection with HTTP2 multiplexing +curl -LO "https://github.com/rust-marker/marker/releases/download/v0.3.0/$file_stem.{tar.gz,sha256}" + +# `--ignore-missing` because the `sha256` file also includes the checksum for `zip` archive +# but users don't need both the `tar.gz` and `zip`. They will chose only one of them. +# This will fail if the checksum of the downloaded file differs from what's specified in `.sha256` +sha256sum --ignore-missing --check $file_stem.sha256 +``` + +## Operating system versions coverage + +We want to cover not only the mainstream operating systems, but also some reasonable range of their versions. For example, for `ubuntu` we want our binaries to work on the current LTS version of `ubuntu` and also on the version of `ubuntu` that precedes it. At the time of this writing the latest LTS version of `ubuntu` is `22.04`, and the previous one is `20.04`, while the LTS version of ubuntu before that one is `18.04` and it already reached the "end of standard support" date, and so far partially noone uses it. + +The platforms that we use for builds are pinned to specific OS versions. It gives us confidence that our binaries will run on these platforms or more modern ones. Operating systems are usually backward-compatible, but they are not forward-compatible. + +It means, you can't compile a binary on `ubuntu-22.04` and run it on `ubuntu-20.04` because the GLIBC that the binary will require will be too high for `ubuntu-20.04`. The version of GLIBC installed on `ubuntu-20.04` is `2.29`, and the version of GLIBC installed on `ubuntu-22.04` is `2.34`. This is based on the experience of the problem that appeared in [nushell](https://github.com/nushell/nushell/issues/7282) project. + +> The story of `nushell` was the following. +> +> They used `ubuntu-latest` to build their pre-compiled binaries. At that moment `ubuntu-latest` was aliased to `20.04` and the whole world was sitting on this version of `ubuntu` and everything was fine. +> +> But then some day `ubuntu-22.04` became generally available on GitHub runners and `ubuntu-latest` was changed to point to this new version of `ubuntu`. When the new release of `nushell` was created they built it on `ubuntu-latest` a.k.a. `ubuntu-22.04` at that point in time. +> +> The whole world was still sitting on `ubuntu-20.04` and lazily migrating to `ubuntu-22.04` so, consequently, there were multiple users who wanted to upgrade `nushell` but they were still sitting on `ubuntu-20.04`. The binaries from the new `nushell` release were not working for them due to the higher GLIBC requirement. + +You can check for yourself the GLIBC version requirement of your binary with the following command. + +```bash +objdump -T ./path/to/binary | grep -Eo 'GLIBC_\S+' | sort -V | tail -1 +``` + +### Universal `musl` binaries + +The binaries with `musl` in their name don't depend on GLIBC. They are statically linked meaning they have no dynamic library dependencies. This way they support a wide range of `linux` distributions meaning that you may expect them to run almost on any `linux` distro of any version. + +This comes at a cost, though. The `musl` implementation of `libc` isn't complete, and it may have bugs, performance degradations compared to GLIBC, etc. However, it generally covers everything you may ever need if you aren't doing something unusual. + +# Regular release + +The regular release means a planned event when the maintainer of `marker` publishes the new version of the artifacts to the users. It may happen at any time when such a decision is made, which usually means on some consistent schedule. + +The release process is semi-automated, and thus requires the human involvement. + +The maintainer has to decide what the next release semver version number will be and prepare the `CHANGELOG.md` file with the description of the new release. The description should follow a consistent structure [like this](https://keepachangelog.com/en/1.0.0/). + +The new versions are always prepended to the top of the changelog file. The numbered version at the top is always considered to be the latest release of `marker`. Before invoking the release automation a human must make sure that a new entry with the new version number was created in the `CHANGELOG.md` file. + +## `release` workflow + +Once the `CHANGELOG.md` is ready in `master` the maintainer can trigger the CI `release` workflow from GitHub "Actions" web page. That workflow contains no input variables to set the version. It fully relies on the `CHANGELOG.md` as an input for that. + +The `release` CI workflow then checks out the `marker`, parses the current release version from the `CHANGELOG.md` file and updates `Cargo.toml`, `Cargo.lock` and various `.rs` and `.md` files with the new version. It uses simple regex patterns with `sed` to edit the files. This logic may break, and thus there is a test on regular CI that makes sure it stays stable. + +The release commit is then assigned a `v{semver}` git tag. After that the workflow sets the next version for the new development cycle with the incremented release version and the `-dev` suffix, and creates a new commit with that. No additional tag at this point is created. + +## `release-on-tag` workflow + +The other CI workflow called `release-on-tag` triggers on the new `v{sermver}` git tag in the repository. It does the following. + +1. It creates a new GitHub release assiated with the new tag. The description of the GitHub release will contain a copy of the section corresponding to the new version from the `CHANGELOG.md`. +2. It builds the pre-compiled binaries for multiple platforms and uploads them to the new GitHub release. +3. It publishes the new versionof packages to `crates.io`. + +Once this workflow finishes successfully, the release process may be considered complete. + +# Hotfix backport release + +There may be a case that a new critical bug was revealed in the released version of `marker` artifacts. It is most likely that bugs come from the new version of code, so you'll probably want to fix the bug in the current version of code present in `master`, increment the patch version and release it. The release flow in this case is identical to the [regular release](#regular-release) with the specificity that the maintainer is expected to make a patch bump in the changelog. + +A more elaborate case is when the bug was fixed in the latest version of `marker` but there still are users who sit on the previous incompatible version of `marker`, and upgrading to the latest one for them may take too much effort and time, but they desperately need a fix today. + +For this case you need to check out the older version of `marker` in a new git branch. Use the git tags to find the proper commit for that. Make the fix in your branch and call it `hotfix/X.Y.Z`, for example. The format of the branch name doesn't matter, but let's use this one just for consistency. + +Push the branch to the upstream `marker` repository. You don't want to merge that branch into `master`, because master is already far ahead of that branch and contains the fix in the new code. You just need to create it in the upstream so that the CI workflows in that repo can use it. + +Add a new entry with the new patch to the `CHANGELOG.md` file in that branch just like during the regular release. Now trigger the `release` CI workflow, but specify your custom hotfix branch in the "Use workflow from" input selector. Once that is done you should also cherry-pick the commit that updated the `CHANGELOG.md` into the `master` branch for the history. This backported fix should still appear at the top of the changelog file, but, of course the next release's version should still be the increment of the previous version within the semver compatibility range set for the current development cycle. diff --git a/marker_adapter/Cargo.toml b/marker_adapter/Cargo.toml index 8ac9204b..91cda21c 100644 --- a/marker_adapter/Cargo.toml +++ b/marker_adapter/Cargo.toml @@ -9,8 +9,6 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] marker_api = { workspace = true, features = ["driver-api"] } marker_error = { workspace = true } diff --git a/marker_api/Cargo.toml b/marker_api/Cargo.toml index e9a887b0..e9008d52 100644 --- a/marker_api/Cargo.toml +++ b/marker_api/Cargo.toml @@ -4,19 +4,18 @@ name = "marker_api" categories = ["development-tools"] description = "Marker's API, designed for stability and usability" -edition = { workspace = true } -keywords = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -version = { workspace = true } - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +edition = { workspace = true } +keywords = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } [dependencies] -visibility = { workspace = true } +visibility = { optional = true, workspace = true } [features] # Some items should only be used by the driver implementing the functionality, # this feature enables the export of these items. Note that this interface is # unstable. All usage of the driver api can change between releases. -driver-api = [] +driver-api = ["dep:visibility"] diff --git a/marker_api/README.md b/marker_api/README.md index c0c741ea..8141c60e 100644 --- a/marker_api/README.md +++ b/marker_api/README.md @@ -45,8 +45,8 @@ To get started, create a new Rust crate that compiles to a library (`cargo init crate-type = ["cdylib"] [dependencies] -marker_api = "" -marker_utils = "" +marker_api = "0.2.1" +marker_utils = "0.2.1" ``` #### src/lib.rs diff --git a/marker_lints/Cargo.toml b/marker_lints/Cargo.toml index 4068ebd6..d3d91502 100644 --- a/marker_lints/Cargo.toml +++ b/marker_lints/Cargo.toml @@ -9,8 +9,6 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [lib] crate-type = ["cdylib"] diff --git a/marker_rustc_driver/Cargo.toml b/marker_rustc_driver/Cargo.toml index dfe8043b..5c5deab1 100644 --- a/marker_rustc_driver/Cargo.toml +++ b/marker_rustc_driver/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "marker_rustc_driver" -build = "build.rs" description = "Marker's lint driver for rustc" edition = { workspace = true } @@ -10,8 +9,6 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] marker_adapter = { workspace = true } marker_api = { workspace = true, features = ["driver-api"] } diff --git a/marker_uilints/Cargo.toml b/marker_uilints/Cargo.toml index d2eaf808..3e118fc7 100644 --- a/marker_uilints/Cargo.toml +++ b/marker_uilints/Cargo.toml @@ -8,8 +8,6 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [lib] crate-type = ["cdylib"] diff --git a/marker_uitest/Cargo.toml b/marker_uitest/Cargo.toml index 9291d40a..5bdd67bb 100644 --- a/marker_uitest/Cargo.toml +++ b/marker_uitest/Cargo.toml @@ -3,13 +3,12 @@ name = "marker_uitest" description = "A thin wrapper around the ui_test crate for Marker" -edition = { workspace = true } -keywords = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -version = { workspace = true } - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +edition = { workspace = true } +keywords = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } [dependencies] semver = { workspace = true } diff --git a/marker_uitest/README.md b/marker_uitest/README.md index 3167d743..338b7d30 100644 --- a/marker_uitest/README.md +++ b/marker_uitest/README.md @@ -30,7 +30,7 @@ First add `marker_utils` to the dev-dependencies of the lint crate, and specify ```toml [dev-dependencies] -marker_uitest = "" +marker_uitest = "0.2.1" [[test]] name = "uitest" @@ -47,7 +47,7 @@ use std::{env, path::Path}; fn main() -> color_eyre::Result<()> { let mut config = marker_uitest::simple_ui_test_config!()?; - + // Allows you to automatically update `.stderr` and `.stdout` files let bless = env::var_os("RUST_BLESS").is_some() || env::args().any(|arg| arg == "--bless"); if bless { @@ -79,4 +79,3 @@ Copyright (c) 2022-2023 Rust-Marker Rust-marker is distributed under the terms of the MIT license or the Apache License (Version 2.0). See [LICENSE-APACHE](https://github.com/rust-marker/marker/blob/master/LICENSE-APACHE), [LICENSE-MIT](https://github.com/rust-marker/marker/blob/master/LICENSE-MIT). - diff --git a/marker_utils/Cargo.toml b/marker_utils/Cargo.toml index a14d06f7..784b4c5f 100644 --- a/marker_utils/Cargo.toml +++ b/marker_utils/Cargo.toml @@ -4,13 +4,12 @@ name = "marker_utils" categories = ["development-tools"] description = "Marker's standard library for creating lints" -edition = { workspace = true } -keywords = { workspace = true } -license = { workspace = true } -repository = { workspace = true } -version = { workspace = true } - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +edition = { workspace = true } +keywords = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +rust-version = { workspace = true } +version = { workspace = true } [dependencies] marker_api = { workspace = true } diff --git a/marker_utils/README.md b/marker_utils/README.md index d1ae98ec..87db202c 100644 --- a/marker_utils/README.md +++ b/marker_utils/README.md @@ -21,7 +21,7 @@ To get started, just include *marker_utils* as a dependency: ```toml [dependencies] -marker_api = "" +marker_api = "0.2.1" ``` You can also add [marker_lints] as a lint crate, designed for this crate: diff --git a/scripts/download/cargo-machete.sh b/scripts/download/cargo-machete.sh index ff8a5a6c..77520c65 100755 --- a/scripts/download/cargo-machete.sh +++ b/scripts/download/cargo-machete.sh @@ -16,4 +16,4 @@ download_and_decompress \ $base_url/$file_stem.tar.gz \ --strip-components 1 $file_stem/cargo-machete -with_log mv cargo-machete$exe ~/.cargo/bin +move_to_path cargo-machete diff --git a/scripts/download/cargo-release.sh b/scripts/download/cargo-release.sh new file mode 100755 index 00000000..b4dc03e0 --- /dev/null +++ b/scripts/download/cargo-release.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -euo pipefail + +script_dir=$(readlink -f $(dirname ${BASH_SOURCE[0]})) + +. $script_dir/lib.sh + +version=0.24.11 + +base_url=https://github.com/crate-ci/cargo-release/releases/download/v$version + +file_stem=cargo-release-v$version-$triple_rust + +download_and_decompress $base_url/$file_stem.tar.gz ./cargo-release + +move_to_path cargo-release diff --git a/scripts/download/lib.sh b/scripts/download/lib.sh index 9c8c4cab..232a4825 100755 --- a/scripts/download/lib.sh +++ b/scripts/download/lib.sh @@ -84,3 +84,7 @@ function curl_with_retry { --retry-delay 30 \ "$@" } + +function move_to_path { + with_log mv "$1" $HOME/.cargo/bin +} diff --git a/scripts/download/mdbook.sh b/scripts/download/mdbook.sh index c781352c..0d9cfd22 100755 --- a/scripts/download/mdbook.sh +++ b/scripts/download/mdbook.sh @@ -12,7 +12,7 @@ base_url=https://github.com/rust-lang/mdBook/releases/download/$version file_stem=mdbook-$version-$triple_rust download_and_decompress $base_url/$file_stem.tar.gz -with_log mv mdbook$exe ~/.cargo/bin +move_to_path mdbook # mdbook-toc version=0.14.1 @@ -20,4 +20,4 @@ base_url=https://github.com/badboy/mdbook-toc/releases/download/$version file_stem=mdbook-toc-$version-$triple_rust download_and_decompress $base_url/$file_stem.tar.gz -with_log mv mdbook-toc$exe ~/.cargo/bin +move_to_path mdbook-toc diff --git a/scripts/download/taplo.sh b/scripts/download/taplo.sh index ef9ea305..d9b758c1 100755 --- a/scripts/download/taplo.sh +++ b/scripts/download/taplo.sh @@ -14,5 +14,5 @@ file_stem=taplo-$os-$arch_rust download_and_decompress $base_url/$file_stem.gz -with_log chmod +x $file_stem$exe -with_log mv $file_stem$exe ~/.cargo/bin/taplo$exe +with_log chmod +x $file_stem +with_log mv $file_stem ~/.cargo/bin/taplo diff --git a/scripts/download/typos.sh b/scripts/download/typos.sh index 1804cc10..a98aab93 100755 --- a/scripts/download/typos.sh +++ b/scripts/download/typos.sh @@ -20,4 +20,4 @@ file_stem=typos-$version-x86_64-unknown-linux-musl download_and_decompress $base_url/$file_stem.tar.gz ./typos -with_log mv typos$exe ~/.cargo/bin +move_to_path typos diff --git a/scripts/lib.sh b/scripts/lib.sh index 50f8c9df..ad952b1e 100755 --- a/scripts/lib.sh +++ b/scripts/lib.sh @@ -132,3 +132,8 @@ function end_group { >&$global_stdout echo "::endgroup::" fi } + +function die { + >&2 echo -e "\033[31;1m❗️ Error\n$@\033[0m" + exit 1 +} diff --git a/scripts/release/get-next-dev-version.sh b/scripts/release/get-next-dev-version.sh new file mode 100755 index 00000000..62103d03 --- /dev/null +++ b/scripts/release/get-next-dev-version.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -euo pipefail + +version="$1" + +IFS='.' read -r major minor patch <<< "$version" + +((minor++)) + +echo "$major.$minor.0-dev" diff --git a/scripts/release/get-version-from-changelog.sh b/scripts/release/get-version-from-changelog.sh new file mode 100755 index 00000000..533f17a0 --- /dev/null +++ b/scripts/release/get-version-from-changelog.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Parse the first numbered version from the changelog. +# This lives in a file separate from CI scripts so that it could be tested locally. + +grep --perl-regexp --only-matching '## \[\K\d+\.\d+\.\d+' CHANGELOG.md \ + | head --lines 1 diff --git a/scripts/release/qemu-install.sh b/scripts/release/qemu-install.sh new file mode 100755 index 00000000..6b051170 --- /dev/null +++ b/scripts/release/qemu-install.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +set -euo pipefail + +function apt-get-install { + with_backoff apt-get update -y + with_backoff apt-get install -y --no-install-recommends --no-install-suggests "$@" +} + +apt-get-install \ + build-essential \ + ca-certificates \ + curl + +function curl_with_retry { + with_log curl \ + --location \ + --retry 5 \ + --retry-connrefused \ + --retry-delay 30 \ + "$@" +} + +start_group "Installing Rust toolchain $rust_version" +curl_with_retry \ + --proto '=https' \ + --tlsv1.2 \ + -sSf https://sh.rustup.rs \ + | with_log sh -s -- \ + -y \ + --default-toolchain $rust_version \ + --no-modify-path \ + --component rust-src rustc-dev llvm-tools +end_group diff --git a/scripts/release/set-version.sh b/scripts/release/set-version.sh new file mode 100755 index 00000000..f8239cfb --- /dev/null +++ b/scripts/release/set-version.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +set -euo pipefail + +script_dir=$(readlink -f $(dirname ${BASH_SOURCE[0]})) + +. $script_dir/../lib.sh + +new_version="$1" +shift + +commit="" + +while [[ $# -gt 0 ]] +do + case $1 in + --commit) + commit="$2" + shift + shift + ;; + *) + die "Unknown option: $1" + ;; + esac +done + +function replace { + with_log sed --in-place --file - "$1" +} + +# This regex hackery will work if the `version=` field in the Cargo.toml +# lives at the line start and we consistently use workspace inheritance +# for local path dependencies. +replace Cargo.toml <-dev".to_string(), +- api_version: "-dev".to_string(), ++ version: "0.1.0".to_string(), ++ api_version: "0.1.0".to_string(), + } + +=== cargo-marker/src/error.rs === + # An external crate from a registry +-marker_lints = """# ++marker_lints = "0.1.0""# + ), +- cli_example = display::cli(r#"cargo marker --lints "marker_lints = ''""#), ++ cli_example = display::cli(r#"cargo marker --lints "marker_lints = '0.1.0'""#), + lints = "--lints".blue(), + +=== docs/book/src/usage/lint-crate-declaration.md === + # An external crate from a registry +-marker_lints = "" ++marker_lints = "0.1.0" + + # An external crate from a registry +-cargo marker --lint "marker_lints = ''" ++cargo marker --lint "marker_lints = '0.1.0'" + + +=== marker_api/README.md === + [dependencies] +-marker_api = "" +-marker_utils = "" ++marker_api = "0.1.0" ++marker_utils = "0.1.0" + ``` + +=== marker_lints/README.md === + [workspace.metadata.marker.lints] +-marker_lints = "" ++marker_lints = "0.1.0" + ``` + +=== marker_uitest/README.md === + [dev-dependencies] +-marker_uitest = "" ++marker_uitest = "0.1.0" + + +=== marker_utils/README.md === + [dependencies] +-marker_api = "" ++marker_api = "0.1.0" + ``` + [workspace.metadata.marker.lints] +-marker_lints = "" ++marker_lints = "0.1.0" + ``` diff --git a/scripts/release/test.sh b/scripts/release/test.sh new file mode 100755 index 00000000..527f2aa3 --- /dev/null +++ b/scripts/release/test.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# This script is used to test the release automation script that sets the +# new version number in the project files. It does so by copying the repo +# into a temp directory, staging all dirty files, running the script, and +# comparing the diff to a snapshot of the expected diff. + +set -Eeuo pipefail + +script_dir=$(readlink -f $(dirname ${BASH_SOURCE[0]})) + +. $script_dir/../lib.sh + +trap cleanup SIGINT SIGTERM ERR EXIT + +temp_dir=$(mktemp -d) + +function cleanup { + # Unset the trap to prevent an infinite loop + trap - SIGINT SIGTERM ERR EXIT + + with_log rm -rf "$temp_dir" +} + +with_log rsync --archive --exclude-from .gitignore . "$temp_dir" + +with_log pushd "$temp_dir" + +# Make sure any dirty files in the repo at this point don't influence the diff +with_log git add --all + +./scripts/release/set-version.sh '0.1.0' + +snap=scripts/release/snapshot.diff + +# Make the git diff snapshot and sanitize it from the noise and variable parts +actual=$(\ + with_log git diff --unified=1 \ + | grep --invert-match --perl-regexp '^(index)|(@@.*@@ )|(--- .*)|(\+\+\+ .*)' \ + | sed 's/diff --git a\/\(.*\) b\/.*/\n=== \1 ===/' \ + | sed 's/\(^-[^0-9]*\)[0-9]*\.[0-9]*\.[0-9]*\(.*\)/\1\2/' +) + +if [[ -v UPDATE_SNAP ]]; then + with_log popd + echo "$actual" > "$snap" + exit 0 +fi + +err=0 + +echo "$actual" | git diff --no-index --exit-code "$snap" - || err=1 + +if [[ $err == 0 ]]; then + exit 0 +fi + +die "$(cat < "$file.sha256" + +with_backoff gh release upload \ + "$tag" \ + "$file.tar.gz" \ + "$file.zip" \ + "$file.sha256" \ + --clobber diff --git a/scripts/update-toolchain.sh b/scripts/update-toolchain.sh new file mode 100755 index 00000000..bdd6cedb --- /dev/null +++ b/scripts/update-toolchain.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +if [[ $1 != nightly-????-??-?? ]] +then + echo "Please enter a valid toolchain like \`nightly-2022-01-01\`" + exit 1 +fi + +sed -i "s/nightly-2023-08-24/$1/g" \ + ./marker_rustc_driver/src/main.rs \ + ./marker_rustc_driver/README.md \ + ./rust-toolchain.toml \ + ./.github/workflows/* \ + ./util/update-toolchain.sh \ + ./cargo-marker/src/backend/driver.rs \ + ./cargo-marker/README.md diff --git a/util/update-toolchain.sh b/util/update-toolchain.sh deleted file mode 100755 index 3b7d02f8..00000000 --- a/util/update-toolchain.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -if [[ $1 == nightly-????-??-?? ]] -then - sed -i "s/nightly-2023-08-24/$1/g" ./marker_rustc_driver/src/main.rs ./marker_rustc_driver/README.md ./rust-toolchain.toml ./.github/workflows/* ./util/update-toolchain.sh ./cargo-marker/src/backend/driver.rs ./cargo-marker/README.md -else - echo "Please enter a valid toolchain like \`nightly-2022-01-01\`" -fi;