Merge pull request 'fix(ci): skip main gates for non-default-base PRs' (#892) from fix/non-default-base-pr-gates into main
Some checks failed
Block internal-flavored paths / Block forbidden paths (push) Successful in 18s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 36s
Harness Replays / detect-changes (pull_request) Successful in 54s
Harness Replays / detect-changes (push) Successful in 26s
CI / Detect changes (pull_request) Successful in 1m47s
CI / Detect changes (push) Successful in 1m48s
E2E API Smoke Test / detect-changes (push) Successful in 1m47s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m46s
E2E API Smoke Test / detect-changes (pull_request) Successful in 1m54s
E2E Staging Canvas (Playwright) / detect-changes (push) Successful in 1m52s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 27s
Handlers Postgres Integration / detect-changes (push) Successful in 1m45s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (push) Successful in 23s
review-check-tests / review-check.sh regression tests (push) Successful in 29s
publish-runtime-autobump / bump-and-tag (pull_request) Has been skipped
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m40s
Runtime PR-Built Compatibility / detect-changes (push) Successful in 1m19s
publish-runtime-autobump / pr-validate (pull_request) Successful in 1m10s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (push) Successful in 2m4s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 2m36s
Secret scan / Scan diff for credential-shaped strings (push) Successful in 31s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 1m52s
review-check-tests / review-check.sh regression tests (pull_request) Successful in 32s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (push) Successful in 2m59s
lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request) Successful in 2m53s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 49s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 3m5s
qa-review / approved (pull_request) Successful in 32s
gate-check-v3 / gate-check (pull_request) Successful in 52s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 3m37s
security-review / approved (pull_request) Successful in 28s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 1m30s
sop-checklist / all-items-acked (pull_request) [info tier:low] acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, l
sop-checklist-gate / gate (pull_request) Successful in 34s
Ops Scripts Tests / Ops scripts (unittest) (push) Successful in 1m48s
Harness Replays / Harness Replays (pull_request) Successful in 10s
sop-tier-check / tier-check (pull_request) Successful in 31s
Harness Replays / Harness Replays (push) Successful in 10s
CI / Shellcheck (E2E scripts) (push) Successful in 9s
CI / Canvas (Next.js) (push) Successful in 15s
CI / Python Lint & Test (push) Successful in 15s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 1m54s
Handlers Postgres Integration / Handlers Postgres Integration (push) Successful in 13s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (push) Successful in 16s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 41s
E2E API Smoke Test / E2E API Smoke Test (push) Successful in 2m35s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 2m43s
Continuous synthetic E2E (staging) / Synthetic E2E against staging (push) Has started running
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 5m30s
publish-workspace-server-image / build-and-push (push) Successful in 10m29s
CI / Canvas Deploy Reminder (push) Has been skipped
CI / Python Lint & Test (pull_request) Successful in 8m15s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 2m32s
Sweep stale e2e-* orgs (staging) / Sweep e2e orgs (push) Successful in 20s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (push) Failing after 12m9s
CI / Canvas (Next.js) (pull_request) Successful in 15m47s
CI / Platform (Go) (push) Failing after 16m38s
CI / Platform (Go) (pull_request) Failing after 16m34s
publish-workspace-server-image / Production auto-deploy (push) Failing after 4m15s
Staging SaaS smoke (every 30 min) / Staging SaaS smoke (push) Successful in 4m59s
main-red-watchdog / watchdog (push) Successful in 55s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / all-required (push) Successful in 8s
CI / all-required (pull_request) Successful in 3s
gitea-merge-queue / queue (push) Successful in 17s
gate-check-v3 / gate-check (push) Successful in 1m36s
status-reaper / reap (push) Successful in 2m34s

This commit is contained in:
devops-engineer 2026-05-13 21:45:57 +00:00
commit be3ac6dceb
9 changed files with 81 additions and 8 deletions

View File

@ -60,6 +60,7 @@
# Optional:
# REVIEW_CHECK_DEBUG=1 — per-API-call diagnostic lines
# REVIEW_CHECK_STRICT=1 — also require review.commit_id == pr.head.sha
# DEFAULT_BRANCH=main — branch this gate protects; non-default-base PRs no-op
set -euo pipefail
@ -91,7 +92,7 @@ API="https://${GITEA_HOST}/api/v1"
# secret token value in the process table for any process to read via
# /proc/<pid>/cmdline or ps -ef). The curl config file is read by curl
# itself and never appears in the argv of the curl subprocess.
CURL_AUTH_FILE=$(mktemp -p /tmp curl-auth.XXXXXX)
CURL_AUTH_FILE=$(mktemp "${TMPDIR:-/tmp}/curl-auth.XXXXXX")
chmod 600 "$CURL_AUTH_FILE"
printf 'header = "Authorization: token %s"\n' "$GITEA_TOKEN" > "$CURL_AUTH_FILE"
@ -124,13 +125,19 @@ if [ "$HTTP_CODE" != "200" ]; then
fi
PR_AUTHOR=$(jq -r '.user.login // ""' "$PR_JSON")
PR_HEAD_SHA=$(jq -r '.head.sha // ""' "$PR_JSON")
PR_BASE_REF=$(jq -r '.base.ref // ""' "$PR_JSON")
PR_STATE=$(jq -r '.state // ""' "$PR_JSON")
debug "pr_author=${PR_AUTHOR} pr_head=${PR_HEAD_SHA:0:7} pr_state=${PR_STATE}"
DEFAULT_BRANCH="${DEFAULT_BRANCH:-main}"
debug "pr_author=${PR_AUTHOR} pr_head=${PR_HEAD_SHA:0:7} pr_base=${PR_BASE_REF} pr_state=${PR_STATE}"
if [ "$PR_STATE" != "open" ]; then
echo "::notice::PR ${PR_NUMBER} is ${PR_STATE} — exiting 0 (closed PRs do not gate)"
exit 0
fi
if [ "$PR_BASE_REF" != "$DEFAULT_BRANCH" ]; then
echo "::notice::PR ${PR_NUMBER} targets ${PR_BASE_REF:-<unknown>} not ${DEFAULT_BRANCH}${TEAM}-review gate not applicable"
exit 0
fi
if [ -z "$PR_AUTHOR" ] || [ -z "$PR_HEAD_SHA" ]; then
echo "::error::PR ${PR_NUMBER} missing user.login or head.sha — webhook payload malformed"
exit 1

View File

@ -16,6 +16,7 @@ Scenarios:
T7_team_member team membership 204 (member) exit 0
T8_team_not_member team membership 404 (not a member) exit 1
T9_team_403 team membership 403 (token not in team) exit 1
T14_non_default_base open PR targeting staging script exits 0 (no-op)
Usage:
FIXTURE_STATE_DIR=/tmp/x python3 _review_check_fixture.py 8080
@ -82,12 +83,14 @@ class Handler(http.server.BaseHTTPRequestHandler):
"number": int(pr_num),
"state": "closed",
"head": {"sha": "deadbeef0000111122223333444455556666"},
"base": {"ref": "main"},
"user": {"login": "alice"},
})
return self._json(200, {
"number": int(pr_num),
"state": "open",
"head": {"sha": "deadbeef0000111122223333444455556666"},
"base": {"ref": "staging" if sc == "T14_non_default_base" else "main"},
"user": {"login": "alice"},
})

View File

@ -15,6 +15,7 @@
# T11 — bash syntax check (bash -n passes)
# T12 — jq filter: non-author APPROVED → in candidate list; dismissed → excluded
# T13 — missing required env GITEA_TOKEN → exits 1 with error
# T14 — non-default-base PR exits 0 without requiring review
#
# Hostile-self-review (per feedback_assert_exact_not_substring):
# this test MUST FAIL if the script is absent. Verified by running
@ -73,7 +74,7 @@ assert_file_mode() {
return
fi
local got_mode
got_mode=$(stat -c '%a' "$path" 2>/dev/null || echo "000")
got_mode=$(stat -c '%a' "$path" 2>/dev/null || stat -f '%Lp' "$path" 2>/dev/null || echo "000")
if [ "$expected_mode" = "$got_mode" ]; then
echo " PASS $label (mode=$got_mode)"
PASS=$((PASS + 1))
@ -194,8 +195,9 @@ for a in "$@"; do
done
exec /usr/bin/curl "${new_args[@]}"
CURL_SHIM
# Now substitute FIXPORT with the actual port number
sed -i "s/FIXPORT/${FIX_PORT}/g" "$FIXTURE_DIR/bin/curl"
# Now substitute FIXPORT with the actual port number. Use perl rather than
# sed -i so the test runs on both GNU sed and BSD/macOS sed.
perl -0pi -e "s/FIXPORT/${FIX_PORT}/g" "$FIXTURE_DIR/bin/curl"
chmod +x "$FIXTURE_DIR/bin/curl"
# Helper: run the script with fixture environment
@ -210,6 +212,7 @@ run_review_check() {
GITEA_HOST="fixture.local" \
REPO="molecule-ai/molecule-core" \
PR_NUMBER="999" \
DEFAULT_BRANCH="main" \
TEAM="qa" \
TEAM_ID="20" \
REVIEW_CHECK_DEBUG="0" \
@ -253,6 +256,14 @@ T4_RC=$(cat "$FIX_STATE_DIR/last_rc")
assert_eq "T4 exit code 1 (no candidates)" "1" "$T4_RC"
assert_contains "T4 awaiting non-author APPROVE" "awaiting non-author APPROVE" "$T4_OUT"
# T14 — non-default-base PR should not make the default branch red.
echo
echo "== T14 non-default base PR =="
T14_OUT=$(run_review_check "T14_non_default_base")
T14_RC=$(cat "$FIX_STATE_DIR/last_rc")
assert_eq "T14 exit code 0 (non-default base no-op)" "0" "$T14_RC"
assert_contains "T14 not applicable notice" "gate not applicable" "$T14_OUT"
# T5 — only author reviews → exit 1
echo
echo "== T5 only author reviews =="
@ -296,10 +307,10 @@ echo "== T10 CURL_AUTH_FILE =="
# Verify the token-file logic directly: create a temp file with the
# same mktemp pattern, write the header with printf, chmod 600, then assert.
T10_TOKEN="secret-test-token-abc123"
T10_AUTHFILE=$(mktemp -p /tmp curl-auth.test.XXXXXX)
T10_AUTHFILE=$(mktemp "${TMPDIR:-/tmp}/curl-auth.test.XXXXXX")
chmod 600 "$T10_AUTHFILE"
printf 'header = "Authorization: token %s"\n' "$T10_TOKEN" > "$T10_AUTHFILE"
assert_file_mode "T10a mktemp -p /tmp mode 600 (CURL_AUTH_FILE pattern)" "$T10_AUTHFILE" "600"
assert_file_mode "T10a mktemp authfile mode 600 (CURL_AUTH_FILE pattern)" "$T10_AUTHFILE" "600"
assert_file_contains "T10b printf header format (CURL_AUTH_FILE content)" "$T10_AUTHFILE" "Authorization: token secret-test-token-abc123"
assert_file_contains "T10c 'header =' curl-config syntax" "$T10_AUTHFILE" 'header = "Authorization: token '
rm -f "$T10_AUTHFILE"

View File

@ -64,6 +64,7 @@ jobs:
if: github.event_name == 'pull_request_target' || github.event.inputs.pr_number != ''
env:
GITEA_TOKEN: ${{ secrets.SOP_TIER_CHECK_TOKEN || secrets.GITHUB_TOKEN }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
PR_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
POST_COMMENT: ${{ github.event.inputs.post_comment || 'true' }}
run: |
@ -78,6 +79,7 @@ jobs:
if: github.event_name == 'schedule'
env:
GITEA_TOKEN: ${{ secrets.SOP_TIER_CHECK_TOKEN || secrets.GITHUB_TOKEN }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail

View File

@ -158,6 +158,7 @@ jobs:
# pull_request_target → github.event.pull_request.number
# issue_comment → github.event.issue.number
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
TEAM: qa
TEAM_ID: '20'
REVIEW_CHECK_DEBUG: '0'

View File

@ -66,6 +66,7 @@ jobs:
GITEA_HOST: git.moleculesai.app
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
TEAM: security
TEAM_ID: '21'
REVIEW_CHECK_DEBUG: '0'

View File

@ -488,6 +488,21 @@ def run(repo: str, pr_number: int, post_comment: bool = False) -> dict:
owner, name = repo.split("/", 1)
pr = api_get(f"/repos/{owner}/{name}/pulls/{pr_number}")
base_ref = pr.get("base", {}).get("ref", "main")
default_branch = os.environ.get("DEFAULT_BRANCH", "main")
if base_ref != default_branch:
result = {
"verdict": "CLEAR",
"repo": repo,
"pr": pr_number,
"skipped": True,
"reason": (
f"PR targets {base_ref}, not protected default branch "
f"{default_branch}"
),
"timestamp": datetime.now(timezone.utc).isoformat(),
}
print(json.dumps(result, indent=2))
return result
gates = [
signal_1_comment_scan(pr_number, repo),

View File

@ -0,0 +1,34 @@
import importlib.util
import pathlib
SCRIPT = pathlib.Path(__file__).with_name("gate_check.py")
def load_gate_check():
spec = importlib.util.spec_from_file_location("gate_check", SCRIPT)
mod = importlib.util.module_from_spec(spec)
assert spec.loader is not None
spec.loader.exec_module(mod)
return mod
def test_run_skips_pr_not_targeting_default_branch(monkeypatch):
mod = load_gate_check()
def fake_api_get(path):
assert path == "/repos/molecule-ai/molecule-core/pulls/843"
return {
"number": 843,
"base": {"ref": "staging"},
"head": {"sha": "84b9ca3a129075b8d5159eda5e678f68be1af20f"},
}
monkeypatch.setenv("DEFAULT_BRANCH", "main")
monkeypatch.setattr(mod, "api_get", fake_api_get)
result = mod.run("molecule-ai/molecule-core", 843, post_comment=False)
assert result["verdict"] == "CLEAR"
assert result["skipped"] is True
assert "staging" in result["reason"]

View File

@ -80,7 +80,6 @@ func (s *Store) PatchNamespace(ctx context.Context, name string, body contract.N
}
parts = append(parts, fmt.Sprintf("metadata = $%d", idx))
args = append(args, metadata)
idx++ // advance so subsequent fields (if any) get correct positional index
}
query := fmt.Sprintf(`
UPDATE memory_namespaces SET %s