Skip to content

Commit 725c28f

Browse files
Merge pull request #8 from effect-native/feat/publish-v1
feat: npm publishing pipeline with OIDC provenance
2 parents 597d1a7 + 4f89b10 commit 725c28f

File tree

13 files changed

+804
-88
lines changed

13 files changed

+804
-88
lines changed

.github/workflows/release.yml

Lines changed: 103 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,56 +8,67 @@ on:
88
permissions:
99
contents: write
1010
packages: write
11+
id-token: write # Required for npm OIDC provenance
1112

1213
env:
1314
REGISTRY: ghcr.io
1415
IMAGE_NAME: ${{ github.repository }}
1516

1617
jobs:
1718
build:
18-
name: Build binaries
19+
name: Build all binaries (Zig cross-compile)
1920
runs-on: ubuntu-latest
20-
strategy:
21-
matrix:
22-
include:
23-
# Linux builds
24-
- target: x86_64-linux-gnu
25-
platform: linux-x64-gnu
26-
- target: x86_64-linux-musl
27-
platform: linux-x64-musl
28-
- target: aarch64-linux-gnu
29-
platform: linux-arm64-gnu
30-
- target: aarch64-linux-musl
31-
platform: linux-arm64-musl
32-
- target: armv7-linux-gnueabihf
33-
platform: linux-armv7-gnu
34-
- target: armv7-linux-musleabihf
35-
platform: linux-armv7-musl
36-
- target: i386-linux-musl
37-
platform: linux-i386-musl
38-
# macOS builds
39-
- target: x86_64-macos
40-
platform: darwin-x64
41-
- target: aarch64-macos
42-
platform: darwin-arm64
43-
# Windows build
44-
- target: x86_64-windows
45-
platform: win32-x64
4621
steps:
4722
- uses: actions/checkout@v4
4823

4924
- name: Install Zig
5025
uses: mlugg/setup-zig@v1
5126

52-
- name: Build ${{ matrix.platform }}
27+
- name: Build all platforms
5328
run: |
54-
zig build -Dtarget=${{ matrix.target }} -Doptimize=ReleaseSafe
29+
# Zig cross-compiles for any platform from any platform
30+
# No need for matrix - build everything on one runner
31+
32+
TARGETS=(
33+
"x86_64-linux-gnu:linux-x64-gnu"
34+
"x86_64-linux-musl:linux-x64-musl"
35+
"aarch64-linux-gnu:linux-arm64-gnu"
36+
"aarch64-linux-musl:linux-arm64-musl"
37+
"arm-linux-gnueabihf:linux-arm-gnu"
38+
"arm-linux-musleabihf:linux-arm-musl"
39+
"x86_64-macos:darwin-x64"
40+
"aarch64-macos:darwin-arm64"
41+
# Windows - DISABLED: POSIX API usage needs fixing
42+
# "x86_64-windows:win32-x64"
43+
# i386 - DISABLED: Low priority, test after v1.0.0
44+
# "i386-linux-musl:linux-i386-musl"
45+
)
46+
47+
for entry in "${TARGETS[@]}"; do
48+
IFS=':' read -r target platform <<< "$entry"
49+
echo "=== Building $platform (zig target: $target) ==="
50+
51+
zig build -Dtarget="$target" -Doptimize=ReleaseSafe
52+
53+
mkdir -p "release/$platform"
54+
if [[ "$platform" == win32* ]]; then
55+
cp zig-out/bin/ansilust.exe "release/$platform/"
56+
else
57+
cp zig-out/bin/ansilust "release/$platform/"
58+
fi
59+
60+
# Clean for next build
61+
rm -rf zig-out zig-cache
62+
done
63+
64+
echo "=== All builds complete ==="
65+
find release -type f
5566
5667
- name: Upload artifacts
5768
uses: actions/upload-artifact@v4
5869
with:
59-
name: binaries-${{ matrix.platform }}
60-
path: zig-out/*/ansilust*
70+
name: binaries-all
71+
path: release/
6172
retention-days: 1
6273

6374
assemble-npm:
@@ -72,25 +83,28 @@ jobs:
7283
with:
7384
node-version: '20'
7485

75-
- name: Download all artifacts
86+
- name: Download binaries artifact
7687
uses: actions/download-artifact@v4
7788
with:
89+
name: binaries-all
7890
path: artifacts/
7991

80-
- name: Assemble binaries
92+
- name: Show artifact structure
8193
run: |
82-
mkdir -p zig-out
83-
# Extract binaries from artifact structure
84-
find artifacts -name "ansilust*" -type f | while read file; do
85-
basename=$(basename "$file")
86-
cp "$file" "zig-out/$basename"
87-
done
94+
echo "=== Artifact structure ==="
95+
find artifacts -type f
8896
8997
- name: Install dependencies
9098
run: npm install
9199

100+
- name: Extract version from tag
101+
id: version
102+
run: |
103+
VERSION=${GITHUB_REF#refs/tags/v}
104+
echo "version=$VERSION" >> $GITHUB_OUTPUT
105+
92106
- name: Run assembly script
93-
run: node scripts/assemble-npm-packages.js
107+
run: PACKAGE_VERSION=${{ steps.version.outputs.version }} node scripts/assemble-npm-packages.js
94108

95109
- name: Upload npm packages
96110
uses: actions/upload-artifact@v4
@@ -100,9 +114,12 @@ jobs:
100114
retention-days: 1
101115

102116
publish-npm:
103-
name: Publish to npm
117+
name: Publish to npm (OIDC)
104118
needs: assemble-npm
105119
runs-on: ubuntu-latest
120+
permissions:
121+
contents: read
122+
id-token: write # Required for npm OIDC publishing (no NPM_TOKEN needed)
106123
steps:
107124
- uses: actions/checkout@v4
108125

@@ -112,28 +129,44 @@ jobs:
112129
node-version: '20'
113130
registry-url: 'https://registry.npmjs.org'
114131

132+
- name: Upgrade npm for OIDC support
133+
run: npm install -g npm@latest
134+
115135
- name: Download npm packages
116136
uses: actions/download-artifact@v4
117137
with:
118138
name: npm-packages
119139
path: packages/
120140

121-
- name: Publish platform packages
141+
- name: OIDC preflight - ensure no auth tokens
142+
run: |
143+
echo "=== OIDC Preflight ==="
144+
# Remove any existing auth tokens to ensure OIDC is used
145+
for npmrc in "$NPM_CONFIG_USERCONFIG" ~/.npmrc .npmrc; do
146+
if [ -n "$npmrc" ] && [ -f "$npmrc" ]; then
147+
echo "Cleaning $npmrc of any existing auth tokens..."
148+
sed -i -E '/\/\/registry\.npmjs\.org\/:(_authToken|_auth)\s*=/d' "$npmrc" 2>/dev/null || true
149+
sed -i -E '/^\s*(_authToken|_auth)\s*=/d' "$npmrc" 2>/dev/null || true
150+
fi
151+
done
152+
153+
echo "Verifying npm registry connectivity..."
154+
npm ping || exit 1
155+
echo "Registry: $(npm config get registry)"
156+
157+
- name: Publish platform packages with OIDC
122158
run: |
123159
for pkg in packages/ansilust-*/; do
124160
if [ -f "$pkg/package.json" ]; then
161+
echo "Publishing $pkg..."
125162
cd "$pkg"
126-
npm publish
163+
npm publish --provenance --access public
127164
cd - > /dev/null
128165
fi
129166
done
130-
env:
131-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
132167
133-
- name: Publish meta package
134-
run: npm publish packages/ansilust/
135-
env:
136-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
168+
- name: Publish meta package with OIDC
169+
run: npm publish packages/ansilust/ --provenance --access public
137170

138171
create-release:
139172
name: Create GitHub release
@@ -142,38 +175,44 @@ jobs:
142175
steps:
143176
- uses: actions/checkout@v4
144177

145-
- name: Download all artifacts
178+
- name: Download binaries artifact
146179
uses: actions/download-artifact@v4
147180
with:
181+
name: binaries-all
148182
path: artifacts/
149183

150184
- name: Prepare release artifacts
151185
run: |
152186
mkdir -p release-artifacts
153187
154-
# Copy and organize binaries
155-
find artifacts -name "ansilust*" -type f | while read file; do
156-
platform=$(basename "$file" | sed 's/ansilust-//' | sed 's/.exe$//')
188+
# Iterate through platform directories
189+
for platform_dir in artifacts/*/; do
190+
platform=$(basename "$platform_dir")
191+
binary="$platform_dir/ansilust"
192+
binary_exe="$platform_dir/ansilust.exe"
157193
158-
if [[ "$platform" == "win32"* ]]; then
194+
if [[ -f "$binary_exe" ]]; then
159195
# Windows: create zip
160-
mkdir -p "temp/$platform/bin"
161-
cp "$file" "temp/$platform/bin/"
196+
mkdir -p "temp/ansilust-$platform"
197+
cp "$binary_exe" "temp/ansilust-$platform/"
162198
cd temp
163-
zip -r "../release-artifacts/ansilust-$platform.zip" "$platform/"
199+
zip -r "../release-artifacts/ansilust-$platform.zip" "ansilust-$platform/"
164200
cd ..
165201
rm -rf temp
166-
else
202+
elif [[ -f "$binary" ]]; then
167203
# Unix: create tar.gz
168-
mkdir -p "temp/$platform/bin"
169-
cp "$file" "temp/$platform/bin/ansilust"
170-
chmod +x "temp/$platform/bin/ansilust"
204+
mkdir -p "temp/ansilust-$platform"
205+
cp "$binary" "temp/ansilust-$platform/"
206+
chmod +x "temp/ansilust-$platform/ansilust"
171207
cd temp
172-
tar -czf "../release-artifacts/ansilust-$platform.tar.gz" "$platform/"
208+
tar -czf "../release-artifacts/ansilust-$platform.tar.gz" "ansilust-$platform/"
173209
cd ..
174210
rm -rf temp
175211
fi
176212
done
213+
214+
echo "=== Release artifacts ==="
215+
ls -la release-artifacts/
177216
178217
- name: Generate checksums
179218
run: bash scripts/generate-checksums.sh release-artifacts/
@@ -242,9 +281,10 @@ jobs:
242281
steps:
243282
- uses: actions/checkout@v4
244283

245-
- name: Download all artifacts
284+
- name: Download binaries artifact
246285
uses: actions/download-artifact@v4
247286
with:
287+
name: binaries-all
248288
path: artifacts/
249289

250290
- name: Set up Docker Buildx

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ package-lock.json
88
npm-debug.log*
99
yarn-error.log*
1010

11-
# Platform package binaries
12-
packages/*/bin/
11+
# Platform package binaries (but not the launcher.js in ansilust/bin/)
1312
packages/ansilust-*/
13+
!packages/ansilust/
14+
!packages/ansilust/bin/
15+
!packages/ansilust/bin/launcher.js
1416

1517
# Build artifacts
1618
*.o
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Hypothesis: First Publish Strategy
2+
3+
## The Prior
4+
5+
Based on the context:
6+
- We have a working Zig build system
7+
- We have placeholder npm packages published (`ansilust`, `16colors`, `16c`)
8+
- We have a comprehensive spec for esbuild-style npm distribution
9+
- We have install.sh and release.yml workflows sketched out
10+
- We have changesets configured
11+
12+
**I expect the fastest path to "published somewhere usable" is:**
13+
14+
1. **GitHub Releases first** - Binary tarballs with checksums
15+
2. **npm second** - Platform packages + meta launcher
16+
3. **Everything else later** - AUR, Nix, install scripts from ansilust.com
17+
18+
**Assumptions:**
19+
- The Zig build already produces working binaries for at least the native platform
20+
- GitHub Actions can cross-compile to other targets
21+
- The npm packages just need the launcher.js and binaries stuffed in
22+
- We can ship v1.0.0 with just npm + GitHub releases working
23+
24+
**Rationale:**
25+
- GitHub Releases is the simplest - just `gh release create` with binaries
26+
- npm is the most user-visible - `npx ansilust` is the dream command
27+
- install.sh from ansilust.com requires hosting setup (more friction)
28+
- AUR/Nix can wait - smaller audience, more maintenance
29+
30+
**Key Question:** Can we get to `npx ansilust file.ans` working in one session?
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Evidence Log: First Publish Reality Check
2+
3+
## Raw Facts
4+
5+
### [SUPPORTS] Build System Works
6+
- `zig build -Doptimize=ReleaseSafe` completes successfully
7+
- Produces two binaries:
8+
- `zig-out/bin/ansilust` (8.3MB) - ANSI art renderer
9+
- `zig-out/bin/16c` (11.0MB) - 16colors archive downloader
10+
- Both binaries execute correctly on native platform (Linux x64)
11+
12+
### [SUPPORTS] npm Infrastructure Exists
13+
- Root `package.json` with workspaces configured
14+
- Changesets installed (`@changesets/cli`, `@changesets/changelog-github`)
15+
- Three placeholder packages published at v0.0.1:
16+
- `ansilust` - meta package with launcher stub
17+
- `16colors` - placeholder
18+
- `16c` - placeholder
19+
20+
### [SUPPORTS] Release Workflow Exists
21+
- `.github/workflows/release.yml` - full matrix build (10 targets)
22+
- `.github/workflows/changeset-version.yml` - version PR automation
23+
- `scripts/install.sh` - complete bash installer (327 lines)
24+
- `scripts/assemble-npm-packages.js` - platform package assembly script
25+
26+
### [SUPPORTS] Package Structure Defined
27+
- `packages/ansilust/package.json` has:
28+
- `bin: {"ansilust": "bin/launcher.js"}`
29+
- `dependencies: {"detect-libc": "^2.1.2"}`
30+
- `optionalDependencies` for all 10 platform packages
31+
- BUT: One local file reference `"ansilust-linux-x64-gnu": "file:../ansilust-linux-x64-gnu"` - need to fix
32+
33+
### [FALSIFIES] Launcher Not Implemented
34+
- `packages/ansilust/index.js` is just a placeholder console.log
35+
- `packages/ansilust/bin/launcher.js` does NOT exist
36+
- This blocks `npx ansilust` from working
37+
38+
### [FALSIFIES] Platform Packages Not Created
39+
- No `packages/ansilust-*` directories exist (only `ansilust`, `16colors`, `16c`)
40+
- Assembly script exists but hasn't been run
41+
- Would need to create all 10 platform package directories
42+
43+
### [SUPPORTS] Cross-Compilation Targets Defined
44+
Build matrix in release.yml covers:
45+
- linux-x64-gnu, linux-x64-musl
46+
- linux-arm64-gnu, linux-arm64-musl
47+
- linux-armv7-gnu, linux-armv7-musl
48+
- linux-i386-musl
49+
- darwin-x64, darwin-arm64
50+
- win32-x64
51+
52+
### [SUPPORTS] AUR Package Template Exists
53+
- `aur/PKGBUILD` and `aur/.SRCINFO` exist
54+
- Need to verify they reference correct URLs
55+
56+
### [SUPPORTS] Nix Flake Exists
57+
- `flake.nix` at repository root
58+
- Need to verify it's functional
59+
60+
## Critical Gaps
61+
62+
1. **No launcher.js** - npx won't work
63+
2. **No platform packages** - npm install won't get binaries
64+
3. **Local file reference** in optionalDependencies - will break publish
65+
4. **No --version/--help** in ansilust binary - just usage message
66+
5. **Assembly script** expects specific binary locations that don't match CI output
67+
68+
## Quick Wins
69+
70+
1. Create `bin/launcher.js` - ~50 lines of code
71+
2. Create one platform package manually for testing
72+
3. Fix the local file reference in package.json
73+
4. Test `npm link` workflow locally before publishing

0 commit comments

Comments
 (0)