Skip to content

feat(infra): implement tag-based release pipeline#7

Merged
andrewmelchor merged 2 commits intomainfrom
feat(infra)-release-pipeline
Jan 23, 2026
Merged

feat(infra): implement tag-based release pipeline#7
andrewmelchor merged 2 commits intomainfrom
feat(infra)-release-pipeline

Conversation

@andrewmelchor
Copy link
Copy Markdown
Member

Summary

  • Add unified release script (script/release.ts)
  • Add weekly changeset version workflow
  • Add tag-triggered release workflows for core and app
  • Add curl installer with checksum verification
  • Remove auto-publish on push behavior

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring
  • Chore (dependency update, build script changes, etc.)

How Has This Been Tested?

  • Unit tests
  • E2E tests
  • Manual testing (please describe)

Checklist

  • I have followed the contributing guidelines
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly
  • My changes generate no new warnings

- Add unified release script (script/release.ts)
- Add weekly changeset version workflow
- Add tag-triggered release workflows for core and app
- Add curl installer with checksum verification
- Remove auto-publish on push behavior
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 23, 2026

🚀 Preview Deployment

Your changes have been deployed to:

📖 Docs: https://docs-pr-7.t-req.io

This preview will be automatically removed when the PR is closed.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Jan 23, 2026

Greptile Summary

Implements a tag-based release pipeline that separates version management from publishing, replacing the previous auto-publish-on-push behavior. The new workflow uses changesets for weekly version PRs, then manual tag creation triggers GitHub Actions to build, test, and publish both core and app packages.

Key Changes:

  • Renamed release.yml to changeset-version.yml with weekly scheduling for version PRs (no auto-publish)
  • Added release-app.yml triggered by app-v* tags to build multi-platform binaries, create GitHub releases with checksums, and publish to npm
  • Added release-core.yml triggered by core-v* tags to test and publish the core package
  • Created script/release.ts to validate git state and push version tags from package.json
  • Added install bash script for curl-based installation with platform detection and checksum verification

Issues Found:

  • Security: The installer script uses unsafe regex parsing of GitHub API JSON (line 128) instead of jq, which could be vulnerable to injection if API returns malicious content
  • Logic: Release script doesn't verify local branch is up-to-date with remote before creating tags, risking tags from stale code
  • Efficiency: App release workflow builds binaries twice - once explicitly and again in the publish script

The architecture is solid with good separation of concerns (version management → tagging → publishing), proper version verification, and dry-run support throughout.

Confidence Score: 3/5

  • Safe to merge with fixes recommended for security and correctness issues
  • The PR implements a well-architected release pipeline with good separation of concerns, but has a critical security vulnerability in the installer script (shell injection risk from unsafe JSON parsing) and a logic bug in the release script (missing check for outdated local branches). The workflow files are well-structured with proper version verification and dry-run support. While the infrastructure changes are solid, the two identified bugs could cause real problems in production - the security issue in the installer and the potential for releasing from stale code.
  • install script needs immediate attention for security fix; script/release.ts needs logic fix for branch validation

Important Files Changed

Filename Overview
.github/workflows/release-app.yml Enhanced to trigger on app-v* tags, verifies version matches package.json, creates GitHub releases with binaries and checksums
script/release.ts New unified release script that validates git state, creates tags from package.json versions, and triggers GitHub Actions
install New curl-to-bash installer with platform detection, checksum verification, and shell PATH configuration

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant Local as Local Git
    participant Script as release.ts
    participant Remote as GitHub Remote
    participant GHA as GitHub Actions
    participant NPM as NPM Registry
    participant User as End User

    Note over Dev,GHA: Weekly Version Preparation
    Dev->>Remote: Merge changesets to main
    GHA->>GHA: changeset-version.yml (weekly/manual)
    GHA->>Remote: Create "Version Packages" PR
    Dev->>Remote: Review and merge version PR

    Note over Dev,NPM: Tag-Based Release Flow
    Dev->>Script: bun run release:core/app
    Script->>Local: Validate clean working dir
    Script->>Local: Verify on main branch
    Script->>Local: Read version from package.json
    Script->>Local: Check tag doesn't exist
    Script->>Remote: Push tag (core-v* or app-v*)
    
    Note over GHA,NPM: Core Package Release
    Remote->>GHA: Trigger release-core.yml
    GHA->>GHA: Extract version from tag
    GHA->>GHA: Verify version matches package.json
    GHA->>GHA: Build and test
    GHA->>NPM: Publish @t-req/core
    
    Note over GHA,User: App Package Release
    Remote->>GHA: Trigger release-app.yml
    GHA->>GHA: Extract version from tag
    GHA->>GHA: Verify version matches package.json
    GHA->>GHA: Build all platform binaries
    GHA->>GHA: Create archives with checksums
    GHA->>Remote: Create draft GitHub release
    GHA->>Remote: Upload binary archives
    GHA->>NPM: Publish platform packages
    GHA->>NPM: Publish wrapper package
    GHA->>Remote: Finalize GitHub release
    
    Note over User: Installation
    User->>Remote: curl install script
    User->>Remote: Download binary + checksums
    User->>User: Verify checksum
    User->>User: Install to ~/.treq/bin
Loading

Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile


- name: Publish to npm
if: ${{ github.event_name == 'push' || !inputs.dry-run }}
run: bun run script/publish.ts
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Publishing script is called but may already build binaries again. The packages/app/script/publish.ts runs bun run build:all on line 32, which duplicates the build from line 69-70. This wastes CI time and resources.

Suggested change
run: bun run script/publish.ts
run: bun run script/publish.ts --skip-build
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/release-app.yml
Line: 181:181

Comment:
**style:** Publishing script is called but may already build binaries again. The `packages/app/script/publish.ts` runs `bun run build:all` on line 32, which duplicates the build from line 69-70. This wastes CI time and resources.

```suggestion
        run: bun run script/publish.ts --skip-build
```

How can I resolve this? If you propose a fix, please make it concise.

local response

# Find latest app release (tag starting with app-v)
response=$(curl -fsSL "$api_url" 2>/dev/null) || die "Failed to fetch releases from GitHub"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Response is not sanitized before using grep and sed. If GitHub API returns unexpected data with malicious content, the shell command injection could occur through crafted tag names in the response.

Consider using jq for safe JSON parsing:

Suggested change
response=$(curl -fsSL "$api_url" 2>/dev/null) || die "Failed to fetch releases from GitHub"
response=$(curl -fsSL "$api_url" 2>/dev/null) || die "Failed to fetch releases from GitHub"
version=$(echo "$response" | jq -r '[.[] | select(.tag_name | startswith("app-v")) | .tag_name] | first | sub("^app-v"; "")')
Prompt To Fix With AI
This is a comment left during a code review.
Path: install
Line: 128:128

Comment:
**logic:** Response is not sanitized before using `grep` and `sed`. If GitHub API returns unexpected data with malicious content, the shell command injection could occur through crafted tag names in the response.

Consider using `jq` for safe JSON parsing:

```suggestion
    response=$(curl -fsSL "$api_url" 2>/dev/null) || die "Failed to fetch releases from GitHub"
    version=$(echo "$response" | jq -r '[.[] | select(.tag_name | startswith("app-v")) | .tag_name] | first | sub("^app-v"; "")')
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +167 to +169
if [[ "$expected" != "$actual" ]]; then
die "Checksum verification failed!\nExpected: $expected\nActual: $actual"
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Error message uses \n which may not render as newlines in all shells. The die function uses echo -e which should handle it, but some shells might not expand the escape sequence correctly. Have you tested this on different shells (bash, zsh, dash) to ensure the newline renders correctly?

Prompt To Fix With AI
This is a comment left during a code review.
Path: install
Line: 167:169

Comment:
**style:** Error message uses `\n` which may not render as newlines in all shells. The `die` function uses `echo -e` which should handle it, but some shells might not expand the escape sequence correctly. Have you tested this on different shells (bash, zsh, dash) to ensure the newline renders correctly?

How can I resolve this? If you propose a fix, please make it concise.


// Ensure we have the latest
console.log('\nFetching latest from origin...');
await $`git fetch origin --tags`.quiet();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Fetching tags but not checking if local branch is behind remote. If someone runs this with an outdated local main, they'll create a tag from old code.

Add after this line:

  const behindResult = await $`git rev-list --count HEAD..origin/main`.quiet();
  const behindCount = parseInt(behindResult.text().trim());
  if (behindCount > 0) {
    console.error('Error: Local main branch is behind origin/main');
    console.error(`Please pull the latest changes first (${behindCount} commits behind).`);
    process.exit(1);
  }
Prompt To Fix With AI
This is a comment left during a code review.
Path: script/release.ts
Line: 192:192

Comment:
**logic:** Fetching tags but not checking if local branch is behind remote. If someone runs this with an outdated local `main`, they'll create a tag from old code.

Add after this line:
```
  const behindResult = await $`git rev-list --count HEAD..origin/main`.quiet();
  const behindCount = parseInt(behindResult.text().trim());
  if (behindCount > 0) {
    console.error('Error: Local main branch is behind origin/main');
    console.error(`Please pull the latest changes first (${behindCount} commits behind).`);
    process.exit(1);
  }
```

How can I resolve this? If you propose a fix, please make it concise.

@andrewmelchor andrewmelchor merged commit e485238 into main Jan 23, 2026
2 checks passed
@andrewmelchor andrewmelchor deleted the feat(infra)-release-pipeline branch January 23, 2026 22:14
andrewmelchor added a commit that referenced this pull request Jan 27, 2026
Bump @t-req/core and @t-req/app to v0.2.0

Changes since v0.1.0:
- core: Unified client API (#9), scoped script tokens (#11)
- app: Fixed default port for open command (#10), scoped script tokens (#11), tag-based release pipeline (#7)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant