Skip to content

Commit f9385b7

Browse files
committed
ci: restore provenance.yml publish workflow on main
The v2 monorepo cascade (f6165b0, "chore(ci): land 3 ci changes") removed .github/workflows/provenance.yml from main. Because main is the default branch, that de-registered the "Publish to npm registry" workflow from the Actions UI (no sidebar entry, no Run button) and prune-workflow-runs.yml is wiping its run history. That workflow is still the only publisher for the socket 1.1.x line (current npm latest) shipped from v1.x. Restore the file verbatim from v1.x so the workflow re-registers and is dispatchable again. Dispatch it selecting the v1.x branch as the ref.
1 parent ac1b644 commit f9385b7

1 file changed

Lines changed: 309 additions & 0 deletions

File tree

.github/workflows/provenance.yml

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
name: Publish to npm registry
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
dist-tag:
7+
description: 'npm dist-tag (latest, next, beta, canary, backport, etc.)'
8+
required: false
9+
default: 'latest'
10+
type: string
11+
debug:
12+
description: 'Enable debug output'
13+
required: false
14+
default: '0'
15+
type: string
16+
options:
17+
- '0'
18+
- '1'
19+
20+
permissions:
21+
contents: read
22+
23+
jobs:
24+
build:
25+
name: Build and Publish
26+
runs-on: ubuntu-latest
27+
28+
permissions:
29+
# `contents: write` needed to create the v<version> tag via gh api
30+
# at the end of this job. Token is scoped to the dedicated tag step
31+
# via GH_TOKEN env; never persisted in `.git/config` (checkout keeps
32+
# persist-credentials: false so build/install steps can't reach it).
33+
contents: write
34+
id-token: write # NPM trusted publishing via OIDC
35+
36+
steps:
37+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (2026-05-20)
38+
with:
39+
persist-credentials: false
40+
41+
- name: Install pnpm
42+
shell: bash
43+
run: | # zizmor: ignore[github-env]
44+
PNPM_VERSION="10.33.0"
45+
PNPM_DIR="${RUNNER_TEMP:-/tmp}/pnpm-bin"
46+
KERNEL="$(uname -s | cut -d- -f1)"
47+
ARCH="$(uname -m)"
48+
case "${KERNEL}-${ARCH}" in
49+
Linux-x86_64) ASSET="pnpm-linux-x64" ; EXPECTED_SHA256="8d4e8f7d778e8ac482022e2577011706a872542f6f6f233e795a4d9f978ea8b5" ;;
50+
Linux-aarch64) ASSET="pnpm-linux-arm64" ; EXPECTED_SHA256="06755ad2817548b84317d857d5c8003dc6e9e28416a3ea7467256c49ab400d48" ;;
51+
Darwin-x86_64) ASSET="pnpm-macos-x64" ; EXPECTED_SHA256="c31e29554b0e3f4e03f4617195c949595e4dca36085922003de4896c3ca4057d" ;;
52+
Darwin-arm64) ASSET="pnpm-macos-arm64" ; EXPECTED_SHA256="ed8a1f140f4de457b01ebe0be3ae28e9a7e28863315dcd53d22ff1e5a32d63ae" ;;
53+
MINGW64_NT-x86_64|MSYS_NT-x86_64) ASSET="pnpm-win-x64.exe" ; EXPECTED_SHA256="afc96009dc39fe23a835d65192049e6a995f342496b175585dc2beda7d42d33f" ;;
54+
*) echo "Unsupported platform: ${KERNEL}-${ARCH}" >&2; exit 1 ;;
55+
esac
56+
PNPM_BIN="$PNPM_DIR/$ASSET"
57+
if [ ! -x "$PNPM_BIN" ]; then
58+
mkdir -p "$PNPM_DIR"
59+
curl -fsSL -o "$PNPM_BIN" "https://github.com/pnpm/pnpm/releases/download/v${PNPM_VERSION}/${ASSET}"
60+
ACTUAL_SHA256="$( (sha256sum "$PNPM_BIN" 2>/dev/null || shasum -a 256 "$PNPM_BIN") | cut -d' ' -f1 | tr -d '\\')"
61+
if [ "$ACTUAL_SHA256" != "$EXPECTED_SHA256" ]; then
62+
echo "Checksum mismatch for ${ASSET}!" >&2
63+
echo " Expected: ${EXPECTED_SHA256}" >&2
64+
echo " Actual: ${ACTUAL_SHA256}" >&2
65+
rm -f "$PNPM_BIN"
66+
exit 1
67+
fi
68+
chmod +x "$PNPM_BIN"
69+
# Create pnpm alias. Windows needs a .exe copy; Unix uses a symlink.
70+
if [[ "$ASSET" == *.exe ]]; then
71+
cp "$PNPM_BIN" "$PNPM_DIR/pnpm.exe"
72+
else
73+
ln -sf "$PNPM_BIN" "$PNPM_DIR/pnpm"
74+
fi
75+
fi
76+
echo "$PNPM_DIR" >> "${GITHUB_PATH:-/dev/null}"
77+
78+
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
79+
with:
80+
node-version: 25.9.0
81+
cache: pnpm
82+
registry-url: https://registry.npmjs.org
83+
scope: '@socketsecurity'
84+
85+
- name: Download sfw
86+
shell: bash
87+
env:
88+
GH_TOKEN: ${{ github.token }}
89+
SOCKET_API_KEY: ${{ secrets.SOCKET_API_KEY }} # zizmor: ignore[secrets-outside-env]
90+
run: | # zizmor: ignore[github-env]
91+
# Pinned version + per-platform checksum pairs. Bumping a tool
92+
# requires updating the matching version AND every platform's
93+
# SHA256 in the same commit, otherwise the download / verify
94+
# steps will diverge.
95+
SFW_FREE_VERSION="1.7.2"
96+
SFW_ENTERPRISE_VERSION="1.7.2"
97+
SFW_DIR="${RUNNER_TEMP:-/tmp}/sfw-bin"
98+
KERNEL="$(uname -s | cut -d- -f1)"
99+
ARCH="$(uname -m)"
100+
USE_ENTERPRISE=false
101+
[ -n "$SOCKET_API_KEY" ] && USE_ENTERPRISE=true
102+
if [ "$USE_ENTERPRISE" = "true" ]; then
103+
REPO="SocketDev/firewall-release"
104+
SFW_VERSION="$SFW_ENTERPRISE_VERSION"
105+
case "${KERNEL}-${ARCH}" in
106+
Linux-x86_64) ASSET="sfw-linux-x86_64" ; SFW_BIN="$SFW_DIR/sfw" ; EXPECTED_SHA256="4482b52e6367bd4610519bfd57a104d5907ec87d5399142ed3bb3d222de1f33d" ;;
107+
Linux-aarch64) ASSET="sfw-linux-arm64" ; SFW_BIN="$SFW_DIR/sfw" ; EXPECTED_SHA256="c24a79c27e1a01a59b7a160c165930ae029816c72b141fcfcdb2f73e0774898a" ;;
108+
Darwin-x86_64) ASSET="sfw-macos-x86_64" ; SFW_BIN="$SFW_DIR/sfw" ; EXPECTED_SHA256="da252d2a9a5d0edb271bb771e0d01b9cd6fa1635b6d765f61efd61edb6739f12" ;;
109+
Darwin-arm64) ASSET="sfw-macos-arm64" ; SFW_BIN="$SFW_DIR/sfw" ; EXPECTED_SHA256="b1cdc3bdbd2a3161247bd5cc215eb3c44a90b87fe0b800a33889a14f61bb0d6d" ;;
110+
MINGW64_NT-x86_64|MSYS_NT-x86_64) ASSET="sfw-windows-x86_64.exe" ; SFW_BIN="$SFW_DIR/sfw.exe" ; EXPECTED_SHA256="e52ad806a1c41b440f04098eb1c7e407845f03f5740a6a79006ba6fd172056ec" ;;
111+
*) echo "Unsupported platform: ${KERNEL}-${ARCH}" >&2; exit 1 ;;
112+
esac
113+
else
114+
REPO="SocketDev/sfw-free"
115+
SFW_VERSION="$SFW_FREE_VERSION"
116+
case "${KERNEL}-${ARCH}" in
117+
Linux-x86_64) ASSET="sfw-free-linux-x86_64" ; SFW_BIN="$SFW_DIR/sfw" ; EXPECTED_SHA256="93e2d9dfa244b82a74e014dc26b1c6af18b4adec20f35254378943db5fe91411" ;;
118+
Linux-aarch64) ASSET="sfw-free-linux-arm64" ; SFW_BIN="$SFW_DIR/sfw" ; EXPECTED_SHA256="84a045e4e1bb320cc5c0d3929f02e53f199398b5be0637e8846d02d9ef0027b1" ;;
119+
Darwin-x86_64) ASSET="sfw-free-macos-x86_64" ; SFW_BIN="$SFW_DIR/sfw" ; EXPECTED_SHA256="a5427d479d440f08e3789fa191ba57599be64997196daf42e67d964fec0382b4" ;;
120+
Darwin-arm64) ASSET="sfw-free-macos-arm64" ; SFW_BIN="$SFW_DIR/sfw" ; EXPECTED_SHA256="248fb588e1e1a27e7192f7b079f739fc29a9de61f0bad7e90928363022dc5643" ;;
121+
MINGW64_NT-x86_64|MSYS_NT-x86_64) ASSET="sfw-free-windows-x86_64.exe" ; SFW_BIN="$SFW_DIR/sfw.exe" ; EXPECTED_SHA256="6d333b4cac9d7c5712e2e99677ca634ac8a3020d550c6308312c60bea97f0a28" ;;
122+
*) echo "Unsupported platform: ${KERNEL}-${ARCH}" >&2; exit 1 ;;
123+
esac
124+
fi
125+
if [ ! -x "$SFW_BIN" ]; then
126+
mkdir -p "$SFW_DIR"
127+
DOWNLOAD_URL="$(gh api "repos/${REPO}/releases/tags/v${SFW_VERSION}" \
128+
--jq ".assets[] | select(.name == \"$ASSET\") | .browser_download_url")"
129+
if [ -z "$DOWNLOAD_URL" ]; then
130+
echo "Asset ${ASSET} not found in ${REPO}@v${SFW_VERSION}" >&2
131+
exit 1
132+
fi
133+
curl -fsSL -o "$SFW_BIN" "$DOWNLOAD_URL"
134+
ACTUAL_SHA256="$( (sha256sum "$SFW_BIN" 2>/dev/null || shasum -a 256 "$SFW_BIN") | cut -d' ' -f1 | tr -d '\\')"
135+
if [ "$ACTUAL_SHA256" != "$EXPECTED_SHA256" ]; then
136+
echo "Checksum mismatch for ${ASSET} (${REPO}@v${SFW_VERSION})!" >&2
137+
echo " Expected: ${EXPECTED_SHA256}" >&2
138+
echo " Actual: ${ACTUAL_SHA256}" >&2
139+
rm -f "$SFW_BIN"
140+
exit 1
141+
fi
142+
chmod +x "$SFW_BIN"
143+
fi
144+
echo "SFW_BIN=$SFW_BIN" >> "${GITHUB_ENV:-/dev/null}"
145+
echo "SFW_IS_ENTERPRISE=$USE_ENTERPRISE" >> "${GITHUB_ENV:-/dev/null}"
146+
if [ "$USE_ENTERPRISE" = "true" ]; then
147+
echo "SOCKET_API_KEY=$SOCKET_API_KEY" >> "${GITHUB_ENV:-/dev/null}"
148+
fi
149+
150+
- name: Create sfw shims
151+
shell: bash
152+
run: | # zizmor: ignore[github-env]
153+
SHIM_DIR="${RUNNER_TEMP:-/tmp}/sfw-shim"
154+
rm -rf "$SHIM_DIR"
155+
mkdir -p "$SHIM_DIR"
156+
IS_WINDOWS=false
157+
[[ "$OSTYPE" == msys* || "$OSTYPE" == cygwin* ]] && IS_WINDOWS=true
158+
msys_to_win_path() {
159+
if $IS_WINDOWS && [[ "$1" =~ ^/([a-zA-Z])/(.*) ]]; then
160+
echo "${BASH_REMATCH[1]^^}:\\${BASH_REMATCH[2]//\//\\}"
161+
else
162+
echo "$1"
163+
fi
164+
}
165+
strip_shim_dir() { echo "$PATH" | tr ':' '\n' | grep -vxF "$SHIM_DIR" | paste -sd: -; }
166+
CLEAN_PATH="$(strip_shim_dir)"
167+
# Wrapper mode ecosystems (sfw-free):
168+
# JavaScript/TypeScript: npm, yarn, pnpm
169+
# Python: pip, uv
170+
# Rust: cargo
171+
# https://github.com/SocketDev/sfw-free?tab=readme-ov-file#supported-package-managers
172+
#
173+
# Additional wrapper mode ecosystems (sfw-enterprise):
174+
# Ruby: gem, bundler
175+
# .NET: nuget
176+
# Go: go (Linux only)
177+
# https://github.com/SocketDev/firewall-release/wiki#support-matrix
178+
SSL_WORKAROUND=""
179+
SHIM_CMDS="npm yarn pnpm pip uv cargo"
180+
if [ "$SFW_IS_ENTERPRISE" = "true" ]; then
181+
SHIM_CMDS="npm yarn pnpm pip uv cargo gem bundler nuget"
182+
# Go wrapper mode is only supported on Linux.
183+
[[ "$OSTYPE" == linux* ]] && SHIM_CMDS="$SHIM_CMDS go"
184+
else
185+
SSL_WORKAROUND='export GIT_SSL_NO_VERIFY=true # Workaround: sfw-free does not yet set GIT_SSL_CAINFO.'
186+
fi
187+
for CMD in $SHIM_CMDS; do
188+
REAL="$(PATH="$CLEAN_PATH" command -v "$CMD" 2>/dev/null || true)"
189+
[ -z "$REAL" ] && continue
190+
REAL="$(msys_to_win_path "$REAL")"
191+
SHIM_LINES=('#!/bin/bash' "export PATH=\"\$(echo \"\$PATH\" | tr ':' '\n' | grep -vxF '${SHIM_DIR}' | paste -sd: -)\"")
192+
[ -n "$SSL_WORKAROUND" ] && SHIM_LINES+=("$SSL_WORKAROUND")
193+
SHIM_LINES+=("exec \"${SFW_BIN}\" \"${REAL}\" \"\$@\"")
194+
printf '%s\n' "${SHIM_LINES[@]}" > "$SHIM_DIR/$CMD"
195+
chmod +x "$SHIM_DIR/$CMD"
196+
if $IS_WINDOWS; then
197+
printf '@echo off\r\nset "PATH=;%%PATH%%;"\r\nset "PATH=%%PATH:;%s;=;%%"\r\nset "PATH=%%PATH:~1,-1%%"\r\n"%s" "%s" %%*\r\n' \
198+
"$SHIM_DIR" "$SFW_BIN" "$REAL" > "$SHIM_DIR/$CMD.cmd"
199+
fi
200+
done
201+
echo "$SHIM_DIR" >> "${GITHUB_PATH:-/dev/null}"
202+
echo "SFW_SHIM_DIR=$SHIM_DIR" >> "${GITHUB_ENV:-/dev/null}"
203+
204+
- name: Install dependencies
205+
run: pnpm install --loglevel error
206+
207+
# Compile the Maven manifest extension jar so the dist build bundles it
208+
# into dist/manifest-scripts (the jar is never committed; it ships only in
209+
# the published package). Invoke build-jar.sh directly, NOT via `pnpm run`:
210+
# Socket Firewall wraps the package managers (npm/pnpm/...) it shims, so a
211+
# `pnpm run` would route the Maven wrapper's download through sfw, which
212+
# fails on the non-package fetch. Running bash directly keeps the Maven
213+
# download outside the shimmed process tree. The org action allowlist forbids
214+
# actions/setup-java, so use a JDK pre-installed on the runner image
215+
# (JAVA_HOME_17_X64), falling back to the runner's default `java`.
216+
- name: Build Maven manifest extension jar
217+
run: |
218+
if [ -n "${JAVA_HOME_17_X64:-}" ]; then
219+
export JAVA_HOME="$JAVA_HOME_17_X64"
220+
fi
221+
bash src/commands/manifest/scripts/maven-extension/build-jar.sh
222+
223+
- run: INLINED_SOCKET_CLI_PUBLISHED_BUILD=1 pnpm run build:dist
224+
- name: Publish socket
225+
id: publish_socket
226+
run: npm publish --provenance --access public --tag "${NPM_DIST_TAG}"
227+
continue-on-error: true
228+
env:
229+
NPM_DIST_TAG: ${{ inputs.dist-tag }}
230+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # zizmor: ignore[secrets-outside-env]
231+
SOCKET_CLI_DEBUG: ${{ inputs.debug }}
232+
- run: INLINED_SOCKET_CLI_PUBLISHED_BUILD=1 INLINED_SOCKET_CLI_LEGACY_BUILD=1 pnpm run build:dist
233+
env:
234+
SOCKET_CLI_DEBUG: ${{ inputs.debug }}
235+
- run: npm publish --provenance --access public --tag "${NPM_DIST_TAG}"
236+
continue-on-error: true
237+
env:
238+
NPM_DIST_TAG: ${{ inputs.dist-tag }}
239+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # zizmor: ignore[secrets-outside-env]
240+
SOCKET_CLI_DEBUG: ${{ inputs.debug }}
241+
- run: INLINED_SOCKET_CLI_PUBLISHED_BUILD=1 INLINED_SOCKET_CLI_SENTRY_BUILD=1 pnpm run build:dist
242+
env:
243+
SOCKET_CLI_DEBUG: ${{ inputs.debug }}
244+
- run: npm publish --provenance --access public --tag "${NPM_DIST_TAG}"
245+
continue-on-error: true
246+
env:
247+
NPM_DIST_TAG: ${{ inputs.dist-tag }}
248+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # zizmor: ignore[secrets-outside-env]
249+
SOCKET_CLI_DEBUG: ${{ inputs.debug }}
250+
251+
# Create v<version> git tag at the published commit SHA after a
252+
# successful socket-package publish, idempotently. GitHub Release
253+
# Immutability ("Disallow assets and tags from being modified once a
254+
# release is published") freezes tags once bound to a Release, so:
255+
# - existing tag at same SHA → no-op
256+
# - existing tag at different SHA → hard-fail (operator recovery
257+
# required)
258+
# Gated on the first publish step (publish_socket — the `socket` npm
259+
# package) actually succeeding; the cli / cli-with-sentry publishes
260+
# use `continue-on-error: true` and don't gate the tag.
261+
#
262+
# Uses gh api (not `git push`) so the token only lives in this step's
263+
# env, never written to `.git/config` by an earlier `actions/checkout`
264+
# with persist-credentials: true (which would leak it to every later
265+
# step including `pnpm install` postinstall scripts).
266+
- name: Tag release (idempotent)
267+
if: steps.publish_socket.outcome == 'success'
268+
env:
269+
GH_TOKEN: ${{ github.token }}
270+
REPO: ${{ github.repository }}
271+
run: |
272+
PUBLISHED_SHA=$(git rev-parse HEAD)
273+
PUBLISHED_VERSION=$(node -p "require('./package.json').version")
274+
TAG="v$PUBLISHED_VERSION"
275+
276+
# Look up any existing tag ref. gh api exits non-zero on 404 (tag
277+
# absent) and writes the error body to stdout, so branch on the
278+
# exit code — never on whether stdout is empty. EXISTING_JSON is
279+
# only valid JSON when the call succeeded.
280+
if EXISTING_JSON=$(gh api "repos/$REPO/git/ref/tags/$TAG" 2>/dev/null); then
281+
# The ref's object is either a commit (lightweight tag) or a tag
282+
# object (annotated/signed tag, e.g. the hand-created `git tag -s`
283+
# tags). For an annotated tag, object.sha is the tag-object SHA,
284+
# not the commit — dereference it via git/tags to get the commit
285+
# the tag actually points at before comparing.
286+
REF_TYPE=$(echo "$EXISTING_JSON" | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).object.type")
287+
REF_OBJECT_SHA=$(echo "$EXISTING_JSON" | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).object.sha")
288+
if [ "$REF_TYPE" = "tag" ]; then
289+
EXISTING_SHA=$(gh api "repos/$REPO/git/tags/$REF_OBJECT_SHA" --jq '.object.sha')
290+
else
291+
EXISTING_SHA="$REF_OBJECT_SHA"
292+
fi
293+
if [ "$EXISTING_SHA" = "$PUBLISHED_SHA" ]; then
294+
echo "Tag $TAG already exists at $PUBLISHED_SHA — no-op."
295+
exit 0
296+
fi
297+
echo "::error::Tag $TAG exists at $EXISTING_SHA but publish SHA is $PUBLISHED_SHA."
298+
echo "::error::Release immutability is enabled; this requires manual recovery:"
299+
echo "::error:: 1. Delete any GitHub Release tied to $TAG"
300+
echo "::error:: 2. Delete the tag via the API"
301+
echo "::error:: 3. Re-run this workflow"
302+
exit 1
303+
fi
304+
305+
gh api "repos/$REPO/git/refs" \
306+
-X POST \
307+
-f "ref=refs/tags/$TAG" \
308+
-f "sha=$PUBLISHED_SHA"
309+
echo "Created tag $TAG at $PUBLISHED_SHA"

0 commit comments

Comments
 (0)