Skip to content

ci: drive releases through pull requests - #150

Merged
9romise merged 1 commit into
mainfrom
release
Aug 22, 2026
Merged

ci: drive releases through pull requests#150
9romise merged 1 commit into
mainfrom
release

Conversation

@9romise

@9romise 9romise commented Aug 22, 2026

Copy link
Copy Markdown
Member

No description provided.

@github-actions

Copy link
Copy Markdown

🎉 Package Size Decrease

📦 Package 📏 Base Size 📏 Source Size 📈 Size Change
npmx-language-server 237.8 kB 237.8 kB -1 B

@9romise
9romise added this pull request to the merge queue Aug 22, 2026
Merged via the queue into main with commit f1472a7 Aug 22, 2026
19 checks passed
@9romise
9romise deleted the release branch August 22, 2026 14:50
@9romise 9romise mentioned this pull request Aug 22, 2026
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The release script now passes --pr to bumpp. The publishing workflow now runs when a pull request targeting main closes, but only for merged release/ branches from the same repository. It uses ubuntu-latest, creates a version tag at the pull request merge commit, and normalises secret expression syntax for changelog and extension publishing credentials.

Merge Risk: 🟠 High · up to e9895

The release workflow currently exposes pull-request-controlled commit data to shell parsing and can fail or handle tags incorrectly when a release is retried or races with another job. These issues could compromise release execution or produce incorrect release state, so they should be fixed before merging.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided, so its relevance to the changeset cannot be assessed. Add a short description that explains the release workflow and package.json changes.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch release

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 081a3104-9e4c-4dfa-86d4-022e57837066

📥 Commits

Reviewing files that changed from the base of the PR and between 120a6d7 and e98953c.

📒 Files selected for processing (2)
  • .github/workflows/publish.yml
  • package.json

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment on lines +31 to +33
gh api "repos/$GITHUB_REPOSITORY/git/refs" \
-f ref="refs/tags/v$VERSION" \
-f sha="${{ github.event.pull_request.merge_commit_sha }}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
sed -n '1,90p' .github/workflows/publish.yml
printf '%s\n' '--- related references ---'
rg -n 'git/refs|merge_commit_sha|VERSION|publish' .github README.md 2>/dev/null || true

Repository: npmx-dev/vscode-npmx

Length of output: 2705


🌐 Web query:

GitHub REST API create a reference existing ref conflict get a reference tag object SHA annotated tag documentation

💡 Result:

To create a reference (like a branch or tag) using the GitHub REST API, you use the POST /repos/{owner}/{repo}/git/refs endpoint [1][2]. If you attempt to create a reference that already exists, the API will return a 409 Conflict status code [1][3]. Regarding annotated tags and their object SHAs: 1. An annotated tag in Git is an object that contains its own unique SHA (the tag object SHA), which is distinct from the SHA of the commit it points to [4]. 2. When you retrieve a reference for an annotated tag (e.g., using GET /repos/{owner}/{repo}/git/refs/tags/{tag_name}), the response contains an object block. This block includes the tag's type (which will be "tag") and its object SHA [4]. 3. To get the full details of the annotated tag object (such as the tagger name, date, and the underlying commit SHA), you use the GET /repos/{owner}/{repo}/git/tags/{tag_sha} endpoint, passing the tag object SHA obtained from the reference [5][4]. Creating an annotated tag via the API is a two-step process: first, create the annotated tag object (POST /repos/{owner}/{repo}/git/tags), then create the reference pointing to that object (POST /repos/{owner}/{repo}/git/refs) [5][6]. Creating only the reference without the tag object results in a lightweight tag [5][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
python3 - <<'PY'
import json
import re
import urllib.request
from pathlib import Path

workflow = Path(".github/workflows/publish.yml").read_text()
create = re.search(
    r'gh api "repos/\$GITHUB_REPOSITORY/git/refs"\s*\\\n'
    r'\s*-f ref="refs/tags/v\$VERSION"\s*\\\n'
    r'\s*-f sha="\$\{\{ github\.event\.pull_request\.merge_commit_sha \}\}"',
    workflow,
)
steps = re.findall(r'^\s+- name: (.+)$', workflow, re.MULTILINE)
print("create_request_shape:", bool(create))
print("step_order:", steps)
print("create_before_build:", steps.index("Create tag") < steps.index("Build"))
print("create_before_publish:", steps.index("Create tag") < steps.index("Publish npm packages to registry"))

repo = "npmx-dev/vscode-npmx"
req = urllib.request.Request(
    f"https://api.github.com/repos/{repo}/git/refs/tags",
    headers={"Accept": "application/vnd.github+json", "User-Agent": "read-only-review-probe"},
)
try:
    with urllib.request.urlopen(req, timeout=10) as response:
        refs = json.load(response)
    print("tag_listing_status: 200")
    print("tag_count:", len(refs))
    print("sample_refs:", [item.get("ref") for item in refs[:5]])
except urllib.error.HTTPError as exc:
    print("tag_listing_status:", exc.code)
    print("tag_listing_error:", exc.read().decode("utf-8", "replace")[:300])
except Exception as exc:
    print("tag_listing_error:", type(exc).__name__, str(exc))
PY

Repository: npmx-dev/vscode-npmx

Length of output: 566


Make tag creation safe to retry.

If a later step fails, a rerun receives 409 Conflict because the tag already exists. Read refs/tags/v$VERSION first. Continue only when it points to the merge commit. Fail when it points elsewhere. If creation races with another job, re-read the tag after 409 and apply the same comparison. Dereference annotated tags before comparing their target commit.

🧰 Tools
🪛 zizmor (1.29.0)

[error] 33-33: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

VERSION=$(jq -r '.version' package.json)
gh api "repos/$GITHUB_REPOSITORY/git/refs" \
-f ref="refs/tags/v$VERSION" \
-f sha="${{ github.event.pull_request.merge_commit_sha }}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- workflow files ---'
git ls-files '.github/workflows/*' | sort

printf '%s\n' '--- publish workflow size ---'
wc -l .github/workflows/publish.yml

printf '%s\n' '--- publish workflow relevant sections ---'
cat -n .github/workflows/publish.yml | sed -n '1,75p'

printf '%s\n' '--- zizmor references and merge_commit_sha uses ---'
rg -n -C 3 'zizmor|merge_commit_sha|template-injection|pull_request_target|pull_request' \
  .github pyproject.toml package.json Makefile 2>/dev/null || true

Repository: npmx-dev/vscode-npmx

Length of output: 5310


🌐 Web query:

GitHub pull_request webhook payload merge_commit_sha type documentation zizmor template-injection environment variable remediation

💡 Result:

Regarding GitHub pull_request webhooks, the merge_commit_sha field is technically present in live payloads; however, it has been observed that some recent API schema definitions (e.g., the 2026-03-10 spec) inaccurately omit this field [1]. This discrepancy between the live runtime contract and the formal schema specification can cause issues for tools or integrations relying solely on the generated API schemas [1]. Zizmor is a static analysis tool designed to secure GitHub Actions workflows [2][3]. One of its key capabilities is detecting template injection vulnerabilities, which occur when user-controlled inputs (like issue titles or PR data) are improperly expanded within ${{... }} delimiters in workflow files [4]. Because these expansions happen before execution, they can lead to unintended code injection if placed inside sensitive contexts like run: blocks [4]. To remediate these template injection vulnerabilities, zizmor recommends replacing the direct template expression with an environment variable [4]. The remediation process involves two steps: 1. Defining the environment variable within the step's env: block, assigning it the value of the template expression (e.g., FOO: ${{ github.event.issue.title }}) [5]. 2. Using the shell-specific syntax to reference that variable in the script block (e.g., echo ${FOO} for bash/sh) [4][5]. This approach ensures that the data is handled via the shell's variable expansion mechanisms rather than the GitHub Actions workflow runner's template expansion, effectively preventing the injection [4]. Users should ensure their remediation uses the correct shell syntax (e.g., ${VAR} for sh/bash, %VAR% for cmd, or $env:VAR for PowerShell) [5]. Note that some zizmor versions have refined this logic to preserve case sensitivity for specific contexts, such as existing env.* variables [6].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
import subprocess

script = r'''
set -u
MERGE_COMMIT_SHA_PLACEHOLDER="$1"
# Model the workflow's current shell source after GitHub expression interpolation.
eval 'printf "current:%s\n" "' + MERGE_COMMIT_SHA_PLACEHOLDER + '"'
'''

values = {
    "valid_sha": "0123456789abcdef0123456789abcdef01234567",
    "quoted_sha": '0123456789abcdef0123456789abcdef01234567"; printf "injected"',
    "command_substitution": '$(printf injected)',
}

for name, value in values.items():
    print(f"--- {name} ---")
    result = subprocess.run(
        ["bash", "-c", script, "probe", value],
        text=True,
        capture_output=True,
    )
    print("exit:", result.returncode)
    print("stdout:", result.stdout.rstrip())
    print("stderr:", result.stderr.rstrip())

print("--- environment-variable form ---")
for name, value in values.items():
    result = subprocess.run(
        ["bash", "-c", 'MERGE_COMMIT_SHA="$1"; printf "env:%s\\n" "$MERGE_COMMIT_SHA"', "probe", value],
        text=True,
        capture_output=True,
    )
    print(name, "exit:", result.returncode, "stdout:", result.stdout.rstrip())
PY

Repository: npmx-dev/vscode-npmx

Length of output: 682


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
import subprocess

values = {
    "valid_sha": "0123456789abcdef0123456789abcdef01234567",
    "quoted_sha": '0123456789abcdef0123456789abcdef01234567"; printf "injected"',
    "command_substitution": '$(printf injected)',
}

for name, value in values.items():
    print(f"--- {name}: direct interpolation ---")
    source = f'printf "direct:%s\\n" "{value}"'
    result = subprocess.run(
        ["bash", "-c", source],
        text=True,
        capture_output=True,
    )
    print("exit:", result.returncode)
    print("stdout:", result.stdout.rstrip())
    print("stderr:", result.stderr.rstrip())

    print(f"--- {name}: environment variable ---")
    result = subprocess.run(
        ["bash", "-c", 'printf "env:%s\\n" "$MERGE_COMMIT_SHA"'],
        env={"MERGE_COMMIT_SHA": value},
        text=True,
        capture_output=True,
    )
    print("exit:", result.returncode)
    print("stdout:", result.stdout.rstrip())
    print("stderr:", result.stderr.rstrip())
PY

Repository: npmx-dev/vscode-npmx

Length of output: 833


Keep event data out of shell source.

Pass github.event.pull_request.merge_commit_sha through env and use $MERGE_COMMIT_SHA in the gh api command. This prevents shell injection and addresses the zizmor template-injection finding.

🧰 Tools
🪛 zizmor (1.29.0)

[error] 33-33: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

Source: Linters/SAST tools

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