From 8ef67179baa0201445fc4d66d8995717886d67a2 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Wed, 6 May 2026 21:00:46 -0400 Subject: [PATCH] release: 2.51.0 --- CHANGELOG.md | 62 ++++++++++++ Cargo.lock | 76 ++++++++++++--- Cargo.toml | 19 ++-- README.md | 2 +- crates/squawk_github/src/app.rs | 2 +- crates/xtask/Cargo.toml | 2 + crates/xtask/src/main.rs | 13 ++- crates/xtask/src/update_version.rs | 149 +++++++++++++++++++++++++++++ flake.nix | 2 +- package.json | 2 +- s/update-version | 71 -------------- squawk-vscode/package.json | 2 +- 12 files changed, 301 insertions(+), 101 deletions(-) create mode 100644 crates/xtask/src/update_version.rs delete mode 100755 s/update-version diff --git a/CHANGELOG.md b/CHANGELOG.md index 66b8e71a..98d89cd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,68 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## v2.51.0 - 2026-05-06 + +### Added + +- linter: add file-level override to disable `assume_in_transaction` (#996). Thanks @reteps! + + When using `assume-in-transaction`, you can disable it on a per file basis using a comment: + + ```sql + -- squawk-disable-assume-in-transaction + ``` + +- syntax: add validation for `select into` (#1116) + + Warn about invalid usages, such as: + - `select 4 a union select 5 a into t;` + - `with t as (select 1 a into t) select * from t;` + - `select * from t where a in (select 1 a into t);` + + and more! + +- parser: add empty statement to ast (#1111) + + ```sql + ; + ; + ; + ``` + + now gives: + + ``` + SOURCE_FILE@0..5 + EMPTY_STMT@0..1 + SEMICOLON@0..1 ";" + WHITESPACE@1..2 "\n" + EMPTY_STMT@2..3 + SEMICOLON@2..3 ";" + WHITESPACE@3..4 "\n" + EMPTY_STMT@4..5 + SEMICOLON@4..5 ";" + ``` + +- ide: code actions create table as <-> select into + parser fix (#1113) + + Create table and select into are roughly interchangable, so this code action + supports switching between. + +- ide: show comments for types and columns (#1110) +- ide: hover infer more types for columns (#1109, #1108) + +### Changed + +- parser: cleanup insert & create rule ast grammar to be more strict (#1112) + +### Fixed + +- vscode: fix text mate grammar for multiple stmts on a line (#1115) +- ide: fix infer column name w/ column aliases and goto def (#1118) +- ide: fix output column alias resolution in group by / order by (#1117) +- ide: fix inherit/like resolution + cte fixes (#1107) + ## v2.50.0 - 2026-05-01 ### Added diff --git a/Cargo.lock b/Cargo.lock index 62d9725d..72ac1651 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1227,6 +1227,47 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" +dependencies = [ + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c900ef84826f1338a557697dc8fc601df9ca9af4ac137c7fb61d4c6f2dfd3076" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jod-thread" version = "0.1.2" @@ -1735,6 +1776,15 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.3" @@ -2385,7 +2435,7 @@ dependencies = [ [[package]] name = "squawk" -version = "2.50.0" +version = "2.51.0" dependencies = [ "annotate-snippets", "anyhow", @@ -2416,7 +2466,7 @@ dependencies = [ [[package]] name = "squawk-fmt" -version = "2.50.0" +version = "2.51.0" dependencies = [ "anyhow", "camino", @@ -2432,7 +2482,7 @@ dependencies = [ [[package]] name = "squawk-github" -version = "2.50.0" +version = "2.51.0" dependencies = [ "jsonwebtoken", "log", @@ -2443,7 +2493,7 @@ dependencies = [ [[package]] name = "squawk-ide" -version = "2.50.0" +version = "2.51.0" dependencies = [ "annotate-snippets", "etcetera", @@ -2465,14 +2515,14 @@ dependencies = [ [[package]] name = "squawk-lexer" -version = "2.50.0" +version = "2.51.0" dependencies = [ "insta", ] [[package]] name = "squawk-linter" -version = "2.50.0" +version = "2.51.0" dependencies = [ "annotate-snippets", "enum-iterator", @@ -2487,7 +2537,7 @@ dependencies = [ [[package]] name = "squawk-parser" -version = "2.50.0" +version = "2.51.0" dependencies = [ "annotate-snippets", "camino", @@ -2500,7 +2550,7 @@ dependencies = [ [[package]] name = "squawk-server" -version = "2.50.0" +version = "2.51.0" dependencies = [ "anyhow", "crossbeam-channel", @@ -2526,7 +2576,7 @@ dependencies = [ [[package]] name = "squawk-syntax" -version = "2.50.0" +version = "2.51.0" dependencies = [ "annotate-snippets", "camino", @@ -2540,7 +2590,7 @@ dependencies = [ [[package]] name = "squawk-thread" -version = "2.50.0" +version = "2.51.0" dependencies = [ "crossbeam-channel", "crossbeam-utils", @@ -2552,7 +2602,7 @@ dependencies = [ [[package]] name = "squawk-wasm" -version = "2.50.0" +version = "2.51.0" dependencies = [ "console_error_panic_hook", "console_log", @@ -3428,7 +3478,7 @@ checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547" [[package]] name = "xtask" -version = "2.50.0" +version = "2.51.0" dependencies = [ "anyhow", "camino", @@ -3436,12 +3486,14 @@ dependencies = [ "convert_case", "csv", "enum-iterator", + "jiff", "proc-macro2", "quote", "regex", "rustc-hash 2.1.1", "serde", "serde_json", + "toml", "ungrammar", "xshell", ] diff --git a/Cargo.toml b/Cargo.toml index 9e3a5d36..16fa49dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["crates/*"] resolver = "2" [workspace.package] -version = "2.50.0" +version = "2.51.0" edition = "2024" rust-version = "1.94" authors = ["Squawk Team & Contributors"] @@ -44,6 +44,7 @@ rowan = "0.15.15" salsa = "0.26.0" smol_str = "0.3.2" enum-iterator = "2.1.0" +jiff = "0.2.24" itertools = "0.14.0" line-index = "0.1.2" lsp-server = "0.7.8" @@ -78,14 +79,14 @@ rustc-hash = "2.1.1" # local # we have to make the versions explicit otherwise `cargo publish` won't work -squawk-github = { path = "./crates/squawk_github", version = "2.50.0" } -squawk-ide = { path = "./crates/squawk_ide", version = "2.50.0" } -squawk-lexer = { path = "./crates/squawk_lexer", version = "2.50.0" } -squawk-parser = { path = "./crates/squawk_parser", version = "2.50.0" } -squawk-syntax = { path = "./crates/squawk_syntax", version = "2.50.0" } -squawk-linter = { path = "./crates/squawk_linter", version = "2.50.0" } -squawk-server = { path = "./crates/squawk_server", version = "2.50.0" } -squawk-thread = { path = "./crates/squawk_thread", version = "2.50.0" } +squawk-github = { path = "./crates/squawk_github", version = "2.51.0" } +squawk-ide = { path = "./crates/squawk_ide", version = "2.51.0" } +squawk-lexer = { path = "./crates/squawk_lexer", version = "2.51.0" } +squawk-parser = { path = "./crates/squawk_parser", version = "2.51.0" } +squawk-syntax = { path = "./crates/squawk_syntax", version = "2.51.0" } +squawk-linter = { path = "./crates/squawk_linter", version = "2.51.0" } +squawk-server = { path = "./crates/squawk_server", version = "2.51.0" } +squawk-thread = { path = "./crates/squawk_thread", version = "2.51.0" } [workspace.lints.clippy] collapsible_else_if = "allow" diff --git a/README.md b/README.md index 7ee163b5..2d5ca37a 100644 --- a/README.md +++ b/README.md @@ -265,7 +265,7 @@ to your project's `.pre-commit-config.yaml`: ```yaml repos: - repo: https://github.com/sbdchd/squawk - rev: 2.50.0 + rev: 2.51.0 hooks: - id: squawk files: path/to/postgres/migrations/written/in/sql diff --git a/crates/squawk_github/src/app.rs b/crates/squawk_github/src/app.rs index 9c4c8292..3118e396 100644 --- a/crates/squawk_github/src/app.rs +++ b/crates/squawk_github/src/app.rs @@ -11,7 +11,7 @@ use serde_json::Value; use std::time::Duration; use std::time::{SystemTime, UNIX_EPOCH}; -pub(crate) const SQUAWK_USER_AGENT: &str = "squawk/2.50.0"; +pub(crate) const SQUAWK_USER_AGENT: &str = "squawk/2.51.0"; #[derive(Debug, Serialize)] struct CommentBody { diff --git a/crates/xtask/Cargo.toml b/crates/xtask/Cargo.toml index ff3c915e..7577e2aa 100644 --- a/crates/xtask/Cargo.toml +++ b/crates/xtask/Cargo.toml @@ -13,6 +13,8 @@ anyhow.workspace = true csv.workspace = true clap.workspace = true enum-iterator.workspace = true +jiff.workspace = true +toml.workspace = true serde.workspace = true serde_json.workspace = true convert_case.workspace = true diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index d6b74bf7..ae659eae 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -5,6 +5,7 @@ use codegen::codegen; use new_rule::new_lint; use sync_builtins::sync_builtins; use sync_pg::sync_pg; +use update_version::{UpdateVersionArgs, update_version}; mod codegen; mod keywords; @@ -12,6 +13,7 @@ mod new_rule; mod path; mod sync_builtins; mod sync_pg; +mod update_version; #[derive(Subcommand, Debug)] enum TaskName { @@ -19,10 +21,12 @@ enum TaskName { Codegen, #[command(long_about = "Create a new linter rule")] NewRule(NewRuleArgs), - #[command(long_about = "Fetch the latest kwlist.h and regression suite from Postgres")] - SyncPg, #[command(long_about = "Generate builtins.sql from PostgreSQL pg_type catalog")] SyncBuiltins, + #[command(long_about = "Fetch the latest kwlist.h and regression suite from Postgres")] + SyncPg, + #[command(long_about = "Bump the squawk version across all version-bearing files")] + UpdateVersion(UpdateVersionArgs), } #[derive(Args, Debug)] @@ -42,9 +46,10 @@ struct Arguments { fn main() -> Result<()> { let args = Arguments::parse(); match args.task { - TaskName::SyncPg => sync_pg(), - TaskName::NewRule(args) => new_lint(args), TaskName::Codegen => codegen(), + TaskName::NewRule(args) => new_lint(args), TaskName::SyncBuiltins => sync_builtins(), + TaskName::SyncPg => sync_pg(), + TaskName::UpdateVersion(args) => update_version(args), } } diff --git a/crates/xtask/src/update_version.rs b/crates/xtask/src/update_version.rs new file mode 100644 index 00000000..9aea67bb --- /dev/null +++ b/crates/xtask/src/update_version.rs @@ -0,0 +1,149 @@ +use anyhow::{Context, Result, bail}; +use clap::Args; +use jiff::Zoned; +use regex::Regex; +use xshell::{Shell, cmd}; + +use crate::path::project_root; + +#[derive(Args, Debug)] +pub(crate) struct UpdateVersionArgs { + /// New version, e.g. 2.51.0. If omitted, auto-increments the minor version. + new_version: Option, +} + +pub(crate) fn update_version(args: UpdateVersionArgs) -> Result<()> { + let sh = Shell::new()?; + sh.change_dir(project_root()); + + let new_version = match args.new_version { + Some(v) => v, + None => auto_increment_version(&sh)?, + }; + + cmd!(sh, "git switch master") + .run() + .context("Failed to switch to master branch")?; + + let release_branch = format!("release-{new_version}"); + if cmd!(sh, "git switch {release_branch}") + .ignore_stdout() + .ignore_stderr() + .run() + .is_err() + { + cmd!(sh, "git switch -c {release_branch}").run()?; + } + + update_versions(&sh, &new_version)?; + + println!("Updating CHANGELOG.md..."); + let current_date = Zoned::now().strftime("%Y-%m-%d").to_string(); + + let tags = cmd!(sh, "git tag --sort=-version:refname").read()?; + let latest_tag = tags + .lines() + .next() + .filter(|t| !t.is_empty()) + .context("No previous git tag found")?; + println!("Fetching commits since {latest_tag}..."); + let pretty = "format:- %s"; + let commits = cmd!(sh, "git log {latest_tag}..master --pretty={pretty}").read()?; + + update_changelog(&sh, &new_version, ¤t_date, &commits)?; + + let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string()); + let mut editor_parts = editor.split_whitespace(); + let program = editor_parts.next().context("EDITOR is empty")?; + let extra_args: Vec<&str> = editor_parts.collect(); + cmd!(sh, "{program} {extra_args...} CHANGELOG.md").run()?; + + Ok(()) +} + +fn auto_increment_version(sh: &Shell) -> Result { + let cargo_toml = sh.read_file(project_root().join("Cargo.toml"))?; + let parsed: toml::Value = toml::from_str(&cargo_toml)?; + let version = parsed + .get("workspace") + .and_then(|w| w.get("package")) + .and_then(|p| p.get("version")) + .and_then(|v| v.as_str()) + .context("workspace.package.version not found in Cargo.toml")?; + + let parts: Vec<&str> = version.split('.').collect(); + let [major, minor, _patch] = parts.as_slice() else { + bail!("invalid version format: {version}"); + }; + let minor: u32 = minor.parse().context("invalid minor version")?; + let next = format!("{major}.{}.0", minor + 1); + println!("Auto-incrementing to {next}"); + Ok(next) +} + +fn update_versions(sh: &Shell, v: &str) -> Result<()> { + let version_rep = format!(r#"version = "{v}""#); + replace_in_file(sh, "Cargo.toml", r#"(?m)^version = ".*""#, &version_rep)?; + replace_in_file( + sh, + "Cargo.toml", + r#"(squawk-[a-z_]+ = \{ path = "[^"]+", )version = "[^"]+""#, + &format!(r#"${{1}}version = "{v}""#), + )?; + replace_in_file( + sh, + "Cargo.lock", + r#"(name = "squawk"\n)version = ".*?""#, + &format!(r#"${{1}}version = "{v}""#), + )?; + + let json_rep = format!(r#""version": "{v}""#); + replace_in_file(sh, "package.json", r#""version": ".*""#, &json_rep)?; + replace_in_file( + sh, + "squawk-vscode/package.json", + r#""version": ".*""#, + &json_rep, + )?; + + replace_in_file( + sh, + "flake.nix", + r#"(?s)(pname = "squawk";.*?)version = ".*?""#, + &format!(r#"${{1}}version = "{v}""#), + )?; + replace_in_file( + sh, + "crates/squawk_github/src/app.rs", + r#"const SQUAWK_USER_AGENT: &str = "squawk/.*""#, + &format!(r#"const SQUAWK_USER_AGENT: &str = "squawk/{v}""#), + )?; + replace_in_file(sh, "README.md", "rev: .*", &format!("rev: {v}"))?; + + Ok(()) +} + +fn replace_in_file(sh: &Shell, path: &str, pattern: &str, replacement: &str) -> Result<()> { + let full = project_root().join(path); + let content = sh.read_file(&full)?; + let re = Regex::new(pattern)?; + if !re.is_match(&content) { + bail!("pattern {pattern:?} not found in {path}"); + } + let updated = re.replace_all(&content, replacement); + sh.write_file(&full, updated.as_ref())?; + Ok(()) +} + +fn update_changelog(sh: &Shell, version: &str, date: &str, commits: &str) -> Result<()> { + let path = project_root().join("CHANGELOG.md"); + let content = sh.read_file(&path)?; + let needle = "## [Unreleased]\n"; + if !content.contains(needle) { + bail!("CHANGELOG.md is missing the '## [Unreleased]' header"); + } + let replacement = format!("## [Unreleased]\n\n## v{version} - {date}\n\n{commits}\n"); + let updated = content.replacen(needle, &replacement, 1); + sh.write_file(&path, updated)?; + Ok(()) +} diff --git a/flake.nix b/flake.nix index f7f4bed5..952bcc3b 100644 --- a/flake.nix +++ b/flake.nix @@ -18,7 +18,7 @@ { squawk = final.rustPlatform.buildRustPackage { pname = "squawk"; - version = "2.50.0"; + version = "2.51.0"; cargoLock = { lockFile = ./Cargo.lock; diff --git a/package.json b/package.json index 2737cde4..9845eeec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "squawk-cli", - "version": "2.50.0", + "version": "2.51.0", "description": "linter for PostgreSQL, focused on migrations", "repository": "git@github.com:sbdchd/squawk.git", "author": "Squawk Team & Contributors", diff --git a/s/update-version b/s/update-version deleted file mode 100755 index 7217d770..00000000 --- a/s/update-version +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -# This script requires fastmod to be installed -# fastmod: https://github.com/facebookincubator/fastmod - - -main() { - NEW_VERSION="$1" - - if [ -z "$NEW_VERSION" ]; then - NEW_VERSION=$(awk -F'[".]' '/^version = / {print $2 "." ($3+1) ".0"}' Cargo.toml) - if [ -z "$NEW_VERSION" ]; then - echo "Error: Could not determine current version from workspace Cargo.toml" - exit 1 - fi - - echo "Auto-incrementing to $NEW_VERSION" - fi - - echo "Switching to master branch..." - if ! git switch master; then - echo "Error: Failed to switch to master branch" - exit 1 - fi - - echo "Switching to release branch: release-$NEW_VERSION" - git switch "release-$NEW_VERSION" 2>/dev/null || git switch -c "release-$NEW_VERSION" - echo "updating version to '$NEW_VERSION'..." - fastmod --accept-all '^version = ".*"' 'version = "'$NEW_VERSION'"' Cargo.toml - fastmod --accept-all '(squawk-[a-z_]+ = \{ path = "[^"]+", )version = "[^"]+"' '${1}version = "'$NEW_VERSION'"' Cargo.toml - fastmod --accept-all -m '(name = "squawk"\n)version = ".*?"' '${1}version = "'$NEW_VERSION'"' Cargo.lock - fastmod --accept-all '"version": ".*"' '"version": "'$NEW_VERSION'"' package.json - fastmod --accept-all '"version": ".*"' '"version": "'$NEW_VERSION'"' squawk-vscode/package.json - fastmod --accept-all -m '(pname = "squawk";.*?)version = ".*?"' '${1}version = "'$NEW_VERSION'"' flake.nix - fastmod --accept-all 'const SQUAWK_USER_AGENT: &str = "squawk/.*"' 'const SQUAWK_USER_AGENT: &str = "squawk/'$NEW_VERSION'"' crates/squawk_github/src/app.rs - fastmod --accept-all 'rev: .*' 'rev: '$NEW_VERSION'' README.md - - echo "Updating CHANGELOG.md..." - CURRENT_DATE=$(date +"%Y-%m-%d") - LATEST_TAG=$(git tag --sort=-version:refname | head -1) - - if [ -z "$LATEST_TAG" ]; then - echo "Error: No previous git tag found" - exit 1 - fi - - echo "Fetching commits since $LATEST_TAG..." - COMMITS_FILE=$(mktemp) - git log ${LATEST_TAG}..master --pretty=format:"- %s" > "$COMMITS_FILE" - - TEMP_CHANGELOG=$(mktemp) - awk -v date="$CURRENT_DATE" -v version="$NEW_VERSION" -v commits_file="$COMMITS_FILE" ' - /^## \[Unreleased\]$/ { - print $0 - print "" - print "## v" version " - " date - print "" - while ((getline line < commits_file) > 0) { - print line - } - close(commits_file) - next - } - { print } - ' CHANGELOG.md > "$TEMP_CHANGELOG" && mv "$TEMP_CHANGELOG" CHANGELOG.md - - echo "Opening CHANGELOG.md for editing..." - ${EDITOR:-vi} CHANGELOG.md -} - - -main "$@" diff --git a/squawk-vscode/package.json b/squawk-vscode/package.json index 2b0845d0..adaf35ec 100644 --- a/squawk-vscode/package.json +++ b/squawk-vscode/package.json @@ -12,7 +12,7 @@ "icon": "icon.png", "author": "Squawk Team & Contributors", "license": "(Apache-2.0 OR MIT)", - "version": "2.50.0", + "version": "2.51.0", "engines": { "vscode": "^1.101.0" },