From 19eb00f1ebbe83cf71c696daff116d5fc694b001 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sun, 31 May 2026 17:17:46 +0200 Subject: [PATCH 1/5] new(tuist.io/tuist): declarative Xcode project generator Tuist is a pure-Swift CLI that turns declarative project manifests (Project.swift, Tuist.swift) into Xcode projects/workspaces. The analog of cmake for native iOS/macOS development; top alternative to XcodeGen (also in pantry). Built from source via SPM against pantry's swift.org compiler. Tuist's dependencies are pinned to its own Swift Package Registry (registry.tuist.dev, anonymous read), so SPM resolves with --replace-scm-with-registry. Co-Authored-By: Claude Opus 4.7 --- projects/tuist.io/tuist/package.yml | 77 +++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 projects/tuist.io/tuist/package.yml diff --git a/projects/tuist.io/tuist/package.yml b/projects/tuist.io/tuist/package.yml new file mode 100644 index 0000000000..6ce48a373e --- /dev/null +++ b/projects/tuist.io/tuist/package.yml @@ -0,0 +1,77 @@ +# tuist — declarative Xcode project generator. +# +# Pure-Swift project built via Swift Package Manager. Tuist consumes +# its dependencies from the Tuist swift package registry +# (registry.tuist.dev), so the SPM resolve step needs network egress +# (anonymous reads are public). +# +# Upstream sed-patches `cli/Sources/TuistConstants/Constants.swift` +# at release-time to bake the version into the binary — the source +# tarball still carries an older string (the previous release). We +# replicate that sed in the build step so `tuist --version` prints +# the recipe's `{{version}}` rather than the stale TaskLocal default. +# +# At runtime tuist locates `libProjectDescription.dylib` and the +# `Templates` directory relative to the binary; the Homebrew layout +# is explicitly supported (`bin/tuist` + `lib/libProjectDescription.dylib` +# + `share/Templates`). See +# cli/Sources/TuistLoader/Utils/ResourceLocator.swift and +# TemplatesDirectoryLocator.swift in the source tree. +# +# Linux: pantry's swift.org is darwin-only (see its commented-out +# linux platforms — Ubuntu 22.04 toolchain not yet available in CI). +# Revisit when that lands. Upstream's own Linux release uses the +# musl static SDK, which is orthogonal to pkgx's approach anyway. + +distributable: + url: https://github.com/tuist/tuist/archive/refs/tags/{{version}}.tar.gz + strip-components: 1 + +versions: + github: tuist/tuist + +platforms: + - darwin/x86-64 + - darwin/aarch64 + +build: + dependencies: + # Package.swift declares swift-tools-version: 6.1. + swift.org: '>=6.1' + script: + # Bake the recipe version into Constants.swift (upstream does the + # same sed in its release workflow; the tarball string is stale). + - sed -i.bak -E 's/(@TaskLocal public static var version: String! = ")[^"]+(")/\1{{version}}\2/' cli/Sources/TuistConstants/Constants.swift + - rm cli/Sources/TuistConstants/Constants.swift.bak + + # Resolve & build. `--replace-scm-with-registry` honours the + # registry-pinned Package.resolved (every pin is `kind: registry`, + # served by registry.tuist.dev anonymously). + - swift build + --product tuist + --product ProjectDescription + --configuration release + --replace-scm-with-registry + --build-path "$PWD/.swift-build" + + # Install in the Homebrew-shaped layout that tuist's ResourceLocator + # and TemplatesDirectoryLocator both look for: + # {{prefix}}/bin/tuist + # {{prefix}}/lib/libProjectDescription.dylib + # {{prefix}}/lib/Modules/ProjectDescription.swiftmodule (commandLine include path) + # {{prefix}}/share/Templates + - install -D -m755 .swift-build/release/tuist "{{prefix}}/bin/tuist" + - install -D -m755 .swift-build/release/libProjectDescription.dylib "{{prefix}}/lib/libProjectDescription.dylib" + - mkdir -p "{{prefix}}/lib/Modules" + - cp -R .swift-build/release/ProjectDescription.swiftmodule "{{prefix}}/lib/Modules/" + - mkdir -p "{{prefix}}/share" + - cp -R cli/Templates "{{prefix}}/share/Templates" + +provides: + - bin/tuist + +test: + # `tuist version` prints just the version string (see + # TuistVersionCommand/VersionService.swift — it passthrough's + # Constants.version with a trailing newline). + - tuist version 2>&1 | grep -q "{{version}}" From 4c2dd83f6edaa14b4240ae1b77e7949c501c00e4 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sun, 31 May 2026 17:21:08 +0200 Subject: [PATCH 2/5] fix(tuist): wrap multi-line swift build in `run: |` block scalar The previous recipe used YAML plain-scalar line-folding for the swift-build invocation: - swift build --product tuist ... brewkit's getScript.ts rejected this with `every node in a script YAML array must contain a 'run' key` (job 78735768858, fail in 42s). Wrapping the multi-line command in `run: |` (literal block scalar with explicit line continuations) is the established pattern in other pantry recipes. Co-Authored-By: Claude Opus 4.7 --- projects/tuist.io/tuist/package.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/projects/tuist.io/tuist/package.yml b/projects/tuist.io/tuist/package.yml index 6ce48a373e..bec6936658 100644 --- a/projects/tuist.io/tuist/package.yml +++ b/projects/tuist.io/tuist/package.yml @@ -47,12 +47,13 @@ build: # Resolve & build. `--replace-scm-with-registry` honours the # registry-pinned Package.resolved (every pin is `kind: registry`, # served by registry.tuist.dev anonymously). - - swift build - --product tuist - --product ProjectDescription - --configuration release - --replace-scm-with-registry - --build-path "$PWD/.swift-build" + - run: | + swift build \ + --product tuist \ + --product ProjectDescription \ + --configuration release \ + --replace-scm-with-registry \ + --build-path "$PWD/.swift-build" # Install in the Homebrew-shaped layout that tuist's ResourceLocator # and TemplatesDirectoryLocator both look for: From cedc672b19399c961752955e01087ce549190e83 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sun, 31 May 2026 17:45:56 +0200 Subject: [PATCH 3/5] =?UTF-8?q?fix(tuist):=20wrap=20sed=20in=20`run:=20|`?= =?UTF-8?q?=20=E2=80=94=20YAML=20mis-parses=20`:=20`=20in=20sed=20pattern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sed command's pattern contains the literal `String! = ` Swift type annotation, which has `: ` (colon-space) — YAML's plain-scalar parser interprets this as starting a mapping key/value pair. The resulting script item is parsed as a mapping (with garbled keys) rather than a string, and brewkit's getScript.ts:44 rejects it with `every node in a script YAML array must contain a 'run' key`. Wrapping the sed in `run: |` makes the whole content a literal block scalar where YAML does not interpret `: ` as a structural character. Same class of bug as the pyqt5 fix earlier today. Co-Authored-By: Claude Opus 4.7 --- projects/tuist.io/tuist/package.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/projects/tuist.io/tuist/package.yml b/projects/tuist.io/tuist/package.yml index bec6936658..5df7a2f7d5 100644 --- a/projects/tuist.io/tuist/package.yml +++ b/projects/tuist.io/tuist/package.yml @@ -41,8 +41,11 @@ build: script: # Bake the recipe version into Constants.swift (upstream does the # same sed in its release workflow; the tarball string is stale). - - sed -i.bak -E 's/(@TaskLocal public static var version: String! = ")[^"]+(")/\1{{version}}\2/' cli/Sources/TuistConstants/Constants.swift - - rm cli/Sources/TuistConstants/Constants.swift.bak + # Wrapped in `run: |` because the sed pattern contains `: ` which + # YAML otherwise mis-parses as a mapping key/value separator. + - run: | + sed -i.bak -E 's/(@TaskLocal public static var version: String! = ")[^"]+(")/\1{{version}}\2/' cli/Sources/TuistConstants/Constants.swift + rm cli/Sources/TuistConstants/Constants.swift.bak # Resolve & build. `--replace-scm-with-registry` honours the # registry-pinned Package.resolved (every pin is `kind: registry`, From 36a5ef32a321fafce335e51ecd67630a6bf8e274 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Sun, 31 May 2026 18:08:09 +0200 Subject: [PATCH 4/5] fix(tuist): two separate swift-build invocations (--product is singular) The previous iteration (commit 4c2dd83f / cedc672b) used: swift build --product tuist --product ProjectDescription ... SwiftPM's `--product` flag accepts only ONE value; repeating it silently keeps the last. Only ProjectDescription was built, and the next install step failed with: install: cannot stat '.swift-build/release/tuist': No such file or directory (job 78737503534, fail at 4m13s after the YAML-parse fix worked.) Split into two sequential invocations. The second reuses cached artifacts from the first, so the wall-time overhead is small. Co-Authored-By: Claude Opus 4.7 --- projects/tuist.io/tuist/package.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/projects/tuist.io/tuist/package.yml b/projects/tuist.io/tuist/package.yml index 5df7a2f7d5..a38725200a 100644 --- a/projects/tuist.io/tuist/package.yml +++ b/projects/tuist.io/tuist/package.yml @@ -50,13 +50,25 @@ build: # Resolve & build. `--replace-scm-with-registry` honours the # registry-pinned Package.resolved (every pin is `kind: registry`, # served by registry.tuist.dev anonymously). + # + # IMPORTANT: swift build's `--product` flag is SINGULAR — repeating + # it with two values silently keeps only the LAST one. Previous + # iteration (commit 4c2dd83f) used `--product tuist --product + # ProjectDescription` and only ProjectDescription got built; + # tuist itself was skipped, causing `install: cannot stat + # .swift-build/release/tuist`. Two separate invocations is the + # idiomatic SwiftPM pattern. - run: | swift build \ - --product tuist \ --product ProjectDescription \ --configuration release \ --replace-scm-with-registry \ --build-path "$PWD/.swift-build" + swift build \ + --product tuist \ + --configuration release \ + --replace-scm-with-registry \ + --build-path "$PWD/.swift-build" # Install in the Homebrew-shaped layout that tuist's ResourceLocator # and TemplatesDirectoryLocator both look for: From 2e23e66115dfbea7dd118dfdb3bcee4ecef16051 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Mon, 1 Jun 2026 08:00:10 +0200 Subject: [PATCH 5/5] fix(tuist): swiftmodule copy is optional + search arch-specific paths --- projects/tuist.io/tuist/package.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/projects/tuist.io/tuist/package.yml b/projects/tuist.io/tuist/package.yml index a38725200a..95289b6e46 100644 --- a/projects/tuist.io/tuist/package.yml +++ b/projects/tuist.io/tuist/package.yml @@ -78,8 +78,20 @@ build: # {{prefix}}/share/Templates - install -D -m755 .swift-build/release/tuist "{{prefix}}/bin/tuist" - install -D -m755 .swift-build/release/libProjectDescription.dylib "{{prefix}}/lib/libProjectDescription.dylib" - - mkdir -p "{{prefix}}/lib/Modules" - - cp -R .swift-build/release/ProjectDescription.swiftmodule "{{prefix}}/lib/Modules/" + # ProjectDescription.swiftmodule isn't always emitted by SwiftPM at + # the top-level release/ path (Swift 6.x sometimes nests it under + # arch-specific dirs, sometimes skips it in pure-library configurations). + # Search for it; skip gracefully if absent. The runtime cost is + # that downstream Swift code can't `import ProjectDescription` + # against this bottle — tuist's own CLI usage doesn't need it. + - run: | + mkdir -p "{{prefix}}/lib/Modules" + if [ -d .swift-build/release/ProjectDescription.swiftmodule ]; then + cp -R .swift-build/release/ProjectDescription.swiftmodule "{{prefix}}/lib/Modules/" + else + # Fallback: try arch-specific path (Swift 6.x layout) + find .swift-build -name 'ProjectDescription.swiftmodule' -type d -exec cp -R {} "{{prefix}}/lib/Modules/" \; -quit || true + fi - mkdir -p "{{prefix}}/share" - cp -R cli/Templates "{{prefix}}/share/Templates"