Skip to content

Commit c705c9d

Browse files
authored
Migrate from npm to pnpm package manager (#5044)
Migrates SAF CLI from npm to pnpm package manager with comprehensive CI/CD workflow updates and Dockerfile optimizations. **Key Changes**: - Package manager migration (npm → pnpm) - All CI/CD workflows updated for pnpm - Dockerfile optimized with BuildKit cache mounts - Security improvements (SHA pinning, permissions, --ignore-scripts) - Documentation updated **Testing**: All 695 tests passing on 3 platforms Authored by: Aaron Lippold<lippold@gmail.com>
1 parent 6e0b98e commit c705c9d

13 files changed

Lines changed: 9483 additions & 17465 deletions

.dockerignore

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Git
2+
.git
3+
.gitignore
4+
.gitattributes
5+
6+
# CI/CD
7+
.github
8+
9+
# Node
10+
node_modules
11+
npm-debug.log
12+
yarn-error.log
13+
pnpm-debug.log
14+
15+
# Testing
16+
test
17+
coverage
18+
.nyc_output
19+
20+
# Build artifacts
21+
lib
22+
dist
23+
*.tgz
24+
25+
# Documentation (session files)
26+
*.md
27+
!README.md
28+
!LICENSE.md
29+
30+
# IDE
31+
.vscode
32+
.idea
33+
*.swp
34+
*.swo
35+
.DS_Store
36+
37+
# Environment
38+
.env
39+
.env.*
40+
41+
# Temporary
42+
tmp
43+
temp
44+
*.tmp
45+
*.backup
46+
47+
# Claude/session files
48+
.claude
49+
COMMIT-*.md
50+
RECOVERY-*.md
51+
threshold-*.md
52+
debug-scripts

.github/workflows/build-macos.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,20 @@ jobs:
1212
steps:
1313
- uses: actions/checkout@v4
1414

15+
- name: Install pnpm
16+
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
17+
with:
18+
version: 10.18.0
19+
1520
- name: setup node
1621
uses: actions/setup-node@v4
1722
with:
1823
node-version: "22"
1924
check-latest: true
20-
cache: 'npm'
25+
cache: 'pnpm'
2126

2227
- name: Install project dependencies
23-
run: npm ci --omit=dev
28+
run: pnpm install --frozen-lockfile --prod
2429

2530
- name: Remove testing resources
2631
run: rm -rf test

.github/workflows/build-rpm.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@ jobs:
1919
with:
2020
path: saf
2121

22+
- name: Install pnpm
23+
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
24+
with:
25+
version: 10.18.0
26+
2227
- name: setup node
2328
uses: actions/setup-node@v4
2429
with:
2530
node-version: "22"
2631
check-latest: true
27-
cache: 'npm'
28-
cache-dependency-path: ./saf/package-lock.json
32+
cache: 'pnpm'
33+
cache-dependency-path: ./saf/pnpm-lock.yaml
2934

3035
- name: install build dependencies
3136
run: dnf install -y rpm-build perl-Digest-SHA # rpmbuild, shasum
@@ -34,7 +39,7 @@ jobs:
3439
working-directory: ./saf
3540
run: |
3641
rm -rf test
37-
npm ci --omit=dev
42+
pnpm install --frozen-lockfile --prod
3843
3944
- name: set filename envvar
4045
working-directory: ./saf

.github/workflows/build-windows-linux.yml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ jobs:
1515
with:
1616
path: saf
1717

18+
- name: Install pnpm
19+
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
20+
with:
21+
version: 10.18.0
22+
1823
- name: setup node
1924
uses: actions/setup-node@v4
2025
with:
2126
node-version: "22"
2227
check-latest: true
23-
cache: 'npm'
24-
cache-dependency-path: ./saf/package-lock.json
28+
cache: 'pnpm'
29+
cache-dependency-path: ./saf/pnpm-lock.yaml
2530

2631
- name: Install Nullsoft Scriptable Install System and p7zip
2732
run: sudo apt install nsis p7zip-full
@@ -30,7 +35,10 @@ jobs:
3035
working-directory: ./saf
3136
run: |
3237
rm -rf test
33-
npm ci --omit=dev
38+
pnpm install --frozen-lockfile --prod
39+
40+
- name: Install pnpm globally for oclif pack
41+
run: sudo npm install -g pnpm@10.18.0
3442

3543
- name: Pack Windows Installer
3644
working-directory: ./saf

.github/workflows/coverage.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Test Coverage
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
coverage:
11+
name: Generate and Report Coverage
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
pull-requests: write # For PR comments
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Install pnpm
21+
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
22+
with:
23+
version: 10.18.0
24+
25+
- name: Setup Node.js
26+
uses: actions/setup-node@v4
27+
with:
28+
node-version: "22"
29+
cache: 'pnpm'
30+
31+
- name: Install dependencies
32+
run: pnpm install --frozen-lockfile
33+
34+
- name: Build
35+
run: pnpm run prepack
36+
37+
- name: Run tests with coverage
38+
run: pnpm test -- --coverage
39+
40+
- name: Upload coverage to Codecov
41+
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
42+
with:
43+
files: ./coverage/clover.xml
44+
flags: unittests
45+
name: saf-cli-coverage
46+
fail_ci_if_error: false
47+
48+
- name: Coverage Summary
49+
run: |
50+
echo "## Test Coverage Report" >> $GITHUB_STEP_SUMMARY
51+
echo "" >> $GITHUB_STEP_SUMMARY
52+
if [ -f coverage/coverage-summary.json ]; then
53+
cat coverage/coverage-summary.json >> $GITHUB_STEP_SUMMARY
54+
else
55+
echo "Coverage report generated successfully" >> $GITHUB_STEP_SUMMARY
56+
fi

.github/workflows/e2e-ci.yml

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@ jobs:
1616
steps:
1717
- uses: actions/checkout@v4
1818

19+
- name: Install pnpm
20+
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
21+
with:
22+
version: 10.18.0
23+
1924
- name: Setup Node.js on ${{ matrix.platform }}
2025
uses: actions/setup-node@v4
2126
with:
2227
node-version: "22"
2328
check-latest: true
24-
cache: 'npm'
29+
cache: 'pnpm'
2530

2631
- name: Install cinc-auditor on windows
2732
if: ${{ matrix.platform == 'windows' }}
@@ -39,16 +44,16 @@ jobs:
3944
4045
- name: Install dependencies, compile, and run tests on ${{ matrix.platform }}
4146
run: |
42-
npm ci
43-
npm run prepack
44-
npm run test
47+
pnpm install --frozen-lockfile
48+
pnpm run prepack
49+
pnpm run test
4550
4651
- name: Sanity check that the SAF CLI works with no dev dependencies on windows
4752
if: ${{ matrix.platform == 'windows' }}
4853
shell: pwsh
4954
run: |
50-
npm ci --omit=dev
51-
npm run prepack
55+
pnpm install --frozen-lockfile --prod
56+
pnpm run prepack
5257
$stderrFile = New-TemporaryFile
5358
./bin/run -v 2> $stderrFile.FullName 1> $null
5459
$exitCode = $LASTEXITCODE
@@ -67,8 +72,8 @@ jobs:
6772
- name: Sanity check that the SAF CLI works with no dev dependencies on ubuntu or macos
6873
if: ${{ matrix.platform == 'ubuntu' || matrix.platform == 'macos' }}
6974
run: |
70-
npm ci --omit=dev
71-
npm run prepack
75+
pnpm install --frozen-lockfile --prod
76+
pnpm run prepack
7277
if stderr=$(./bin/run -v 2>&1 1>/dev/null); then
7378
[ -z "$stderr" ] || (printf "Failed: received the following stderr:\n%s\n" "$stderr"; exit 1)
7479
else

.github/workflows/linter.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,20 @@ jobs:
1313
steps:
1414
- uses: actions/checkout@v4
1515

16+
- name: Install pnpm
17+
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
18+
with:
19+
version: 10.18.0
20+
1621
- name: Setup Node.js
1722
uses: actions/setup-node@v4
1823
with:
1924
node-version: "22"
2025
check-latest: true
21-
cache: 'npm'
26+
cache: 'pnpm'
2227

2328
- name: Install project dependencies
24-
run: npm ci
29+
run: pnpm install --frozen-lockfile
2530

2631
- name: Run lint
27-
run: npm run lint:ci
32+
run: pnpm run lint:ci

.github/workflows/push-to-npm-gpr.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,24 @@ jobs:
99
steps:
1010
- uses: actions/checkout@v4
1111

12+
- name: Install pnpm
13+
uses: pnpm/action-setup@7088e561eb65bb68695d245aa206f005ef30921d # v4.1.0
14+
with:
15+
version: 10.18.0
16+
1217
- name: setup node
1318
uses: actions/setup-node@v4
1419
with:
1520
node-version: "22"
1621
check-latest: true
1722
registry-url: "https://registry.npmjs.org"
18-
cache: 'npm'
23+
cache: 'pnpm'
1924

2025
- name: Remove testing resources
2126
run: rm -rf test
2227

2328
- name: Install project dependencies
24-
run: npm ci
29+
run: pnpm install --frozen-lockfile
2530

2631
- name: Pack all items that are published as packages
2732
run: npm pack
@@ -36,7 +41,7 @@ jobs:
3641
with:
3742
node-version: "22"
3843
registry-url: 'https://npm.pkg.github.com'
39-
cache: 'npm'
44+
cache: 'pnpm'
4045

4146
# Publish SAF CLI to GitHub Package Registry
4247
- name: Publish SAF CLI to GPR

Dockerfile

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,45 @@
1+
# syntax=docker/dockerfile:1
12
ARG BASE_CONTAINER=node:22-alpine
23

4+
# ============================================================================
5+
# Builder Stage: Install dependencies and build package
6+
# ============================================================================
37
FROM $BASE_CONTAINER AS builder
48

9+
# Enable corepack for pnpm support
10+
RUN corepack enable
11+
12+
ENV PNPM_HOME="/pnpm" \
13+
PATH="$PNPM_HOME:$PATH"
14+
15+
WORKDIR /build
16+
17+
# Copy dependency manifests first for better layer caching
18+
COPY package.json pnpm-lock.yaml ./
19+
20+
# Use BuildKit cache mount for pnpm store (faster rebuilds)
21+
# Fetch dependencies into cache
22+
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
23+
pnpm fetch --frozen-lockfile
24+
25+
# Install production dependencies offline from cache
26+
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
27+
pnpm install --frozen-lockfile --prod --offline --ignore-scripts
28+
29+
# Copy source code (sensitive files excluded via .dockerignore: .git, .env, coverage, node_modules, etc.)
30+
COPY . .
31+
32+
# Build and pack
33+
RUN pnpm run prepack && \
34+
pnpm pack && \
35+
mv mitre-saf-*.tgz saf.tgz
36+
37+
# ============================================================================
38+
# Runtime Stage: Minimal production image
39+
# ============================================================================
40+
FROM $BASE_CONTAINER AS app
41+
42+
# Metadata labels in final stage
543
LABEL name="SAF" \
644
vendor="The MITRE Corporation" \
745
version="${SAF_VERSION}" \
@@ -11,24 +49,19 @@ LABEL name="SAF" \
1149
docs="https://github.com/mitre/saf" \
1250
run="docker run -d --name ${NAME} ${IMAGE} <args>"
1351

14-
RUN mkdir -p /share
15-
16-
COPY . /build
17-
WORKDIR /build
18-
RUN rm -rf test
19-
RUN npm ci --omit=dev --fetch-timeout=600000
20-
RUN mv "$(npm pack | tail -1)" saf.tgz
21-
22-
FROM $BASE_CONTAINER AS app
23-
24-
COPY --from=builder /build/saf.tgz /build/
25-
RUN npm install -g /build/saf.tgz && npm cache clean --force;
26-
27-
# Useful for CI pipelines
52+
# Install runtime utilities (--no-cache prevents cache creation)
2853
RUN apk add --no-cache bash jq curl ca-certificates yq
2954

55+
# Copy and install packaged CLI
56+
COPY --from=builder /build/saf.tgz /tmp/
57+
RUN npm install -g /tmp/saf.tgz --ignore-scripts && \
58+
npm cache clean --force && \
59+
rm /tmp/saf.tgz
60+
61+
# Run as non-root user
3062
USER node
3163

32-
ENTRYPOINT ["saf"]
33-
VOLUME ["/share"]
3464
WORKDIR /share
65+
VOLUME ["/share"]
66+
67+
ENTRYPOINT ["saf"]

0 commit comments

Comments
 (0)