Skip to content

Commit 77cb035

Browse files
committed
Add npm/node release workflow and configure Sandbox for npm builds
- Create new npm-release-reusable.yml workflow for Node.js projects - Update Sandbox release workflow to use npm workflow instead of Rust - Configure Sandbox with working-directory: website - Add Docker support for Node.js applications with multi-stage builds - Include npm test and build steps in the release process - Version management based on package.json instead of Cargo.toml
1 parent cefb944 commit 77cb035

2 files changed

Lines changed: 243 additions & 0 deletions

File tree

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
name: Reusable NPM Release Workflow
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
repository:
7+
description: 'Target repository (e.g., GoPlasmatic/Sandbox)'
8+
required: true
9+
type: string
10+
working-directory:
11+
description: 'Working directory for npm commands'
12+
required: false
13+
type: string
14+
default: 'website'
15+
node-version:
16+
description: 'Node.js version to use'
17+
required: false
18+
type: string
19+
default: '20'
20+
dry-run:
21+
description: 'Dry run (skip release)'
22+
required: false
23+
type: boolean
24+
default: false
25+
docker-enabled:
26+
description: 'Enable Docker image build and push'
27+
required: false
28+
type: boolean
29+
default: false
30+
docker-image-name:
31+
description: 'Docker image name (required if docker-enabled)'
32+
required: false
33+
type: string
34+
secrets:
35+
GH_PAT:
36+
required: true
37+
ACR_URL:
38+
required: false
39+
ACR_USERNAME:
40+
required: false
41+
ACR_PASSWORD:
42+
required: false
43+
44+
jobs:
45+
release:
46+
runs-on: ubuntu-latest
47+
steps:
48+
- name: Checkout target repository
49+
uses: actions/checkout@v4
50+
with:
51+
repository: ${{ inputs.repository }}
52+
token: ${{ secrets.GH_PAT }}
53+
fetch-depth: 0
54+
55+
- name: Setup Node.js
56+
uses: actions/setup-node@v4
57+
with:
58+
node-version: ${{ inputs.node-version }}
59+
cache: 'npm'
60+
cache-dependency-path: ${{ inputs.working-directory }}/package-lock.json
61+
62+
- name: Install dependencies
63+
working-directory: ${{ inputs.working-directory }}
64+
run: npm ci
65+
66+
- name: Run tests
67+
working-directory: ${{ inputs.working-directory }}
68+
run: npm test --if-present
69+
70+
- name: Build application
71+
working-directory: ${{ inputs.working-directory }}
72+
run: npm run build
73+
74+
- name: Get version from package.json
75+
id: get_version
76+
working-directory: ${{ inputs.working-directory }}
77+
run: |
78+
VERSION=$(node -p "require('./package.json').version")
79+
echo "version=$VERSION" >> $GITHUB_OUTPUT
80+
echo "Version: $VERSION"
81+
82+
- name: Check if tag exists
83+
id: check_tag
84+
run: |
85+
if git rev-parse "v${{ steps.get_version.outputs.version }}" >/dev/null 2>&1; then
86+
echo "exists=true" >> $GITHUB_OUTPUT
87+
echo "Tag v${{ steps.get_version.outputs.version }} already exists"
88+
else
89+
echo "exists=false" >> $GITHUB_OUTPUT
90+
echo "Tag v${{ steps.get_version.outputs.version }} does not exist"
91+
fi
92+
93+
- name: Fail if tag exists
94+
if: steps.check_tag.outputs.exists == 'true'
95+
run: |
96+
echo "Error: Version ${{ steps.get_version.outputs.version }} already exists as a tag"
97+
echo "Please update the version in package.json before releasing"
98+
exit 1
99+
100+
- name: Create Dockerfile if not exists
101+
if: inputs.docker-enabled && !inputs.dry-run
102+
run: |
103+
if [ ! -f Dockerfile ]; then
104+
cat > Dockerfile << 'EOF'
105+
# Build stage
106+
FROM node:${{ inputs.node-version }}-alpine AS builder
107+
108+
WORKDIR /app
109+
110+
# Copy package files
111+
COPY ${{ inputs.working-directory }}/package*.json ./
112+
113+
# Install dependencies
114+
RUN npm ci --only=production
115+
116+
# Copy application files
117+
COPY ${{ inputs.working-directory }}/ ./
118+
119+
# Build the application
120+
RUN npm run build
121+
122+
# Runtime stage
123+
FROM node:${{ inputs.node-version }}-alpine
124+
125+
# Install dumb-init for proper signal handling
126+
RUN apk add --no-cache dumb-init
127+
128+
# Create non-root user
129+
RUN addgroup -g 1001 -S nodejs && \
130+
adduser -S nodejs -u 1001
131+
132+
WORKDIR /app
133+
134+
# Copy built application from builder
135+
COPY --from=builder --chown=nodejs:nodejs /app ./
136+
137+
# Switch to non-root user
138+
USER nodejs
139+
140+
# Expose port (adjust as needed)
141+
EXPOSE 3000
142+
143+
# Use dumb-init to handle signals properly
144+
ENTRYPOINT ["dumb-init", "--"]
145+
146+
# Start the application
147+
CMD ["npm", "start"]
148+
EOF
149+
echo "Created Dockerfile"
150+
else
151+
echo "Dockerfile already exists"
152+
fi
153+
154+
- name: Build Docker image
155+
if: inputs.docker-enabled && !inputs.dry-run
156+
run: |
157+
docker build -t ${{ inputs.docker-image-name }}:${{ steps.get_version.outputs.version }} .
158+
docker tag ${{ inputs.docker-image-name }}:${{ steps.get_version.outputs.version }} ${{ inputs.docker-image-name }}:latest
159+
160+
- name: Log in to Azure Container Registry
161+
if: inputs.docker-enabled && !inputs.dry-run
162+
uses: azure/docker-login@v1
163+
with:
164+
login-server: ${{ secrets.ACR_URL }}
165+
username: ${{ secrets.ACR_USERNAME }}
166+
password: ${{ secrets.ACR_PASSWORD }}
167+
168+
- name: Push Docker image to ACR
169+
if: inputs.docker-enabled && !inputs.dry-run
170+
run: |
171+
docker tag ${{ inputs.docker-image-name }}:${{ steps.get_version.outputs.version }} ${{ secrets.ACR_URL }}/${{ inputs.docker-image-name }}:${{ steps.get_version.outputs.version }}
172+
docker tag ${{ inputs.docker-image-name }}:latest ${{ secrets.ACR_URL }}/${{ inputs.docker-image-name }}:latest
173+
docker push ${{ secrets.ACR_URL }}/${{ inputs.docker-image-name }}:${{ steps.get_version.outputs.version }}
174+
docker push ${{ secrets.ACR_URL }}/${{ inputs.docker-image-name }}:latest
175+
176+
- name: Create and push tag
177+
if: '!inputs.dry-run'
178+
run: |
179+
git config user.name github-actions
180+
git config user.email github-actions@github.com
181+
git tag -a "v${{ steps.get_version.outputs.version }}" -m "Release version ${{ steps.get_version.outputs.version }}"
182+
git push origin "v${{ steps.get_version.outputs.version }}"
183+
184+
- name: Create GitHub Release
185+
if: '!inputs.dry-run'
186+
uses: actions/create-release@v1
187+
env:
188+
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
189+
with:
190+
tag_name: v${{ steps.get_version.outputs.version }}
191+
release_name: Release v${{ steps.get_version.outputs.version }}
192+
body: |
193+
## Release v${{ steps.get_version.outputs.version }}
194+
195+
### Changes
196+
- See commit history for changes
197+
198+
### Docker Image
199+
${{ inputs.docker-enabled && format('Docker image available at: `{0}/{1}:{2}`', secrets.ACR_URL, inputs.docker-image-name, steps.get_version.outputs.version) || 'No Docker image for this release' }}
200+
draft: false
201+
prerelease: false
202+
203+
- name: Summary
204+
run: |
205+
echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
206+
echo "" >> $GITHUB_STEP_SUMMARY
207+
if [ "${{ inputs.dry-run }}" == "true" ]; then
208+
echo "**DRY RUN MODE** - No actual release was created" >> $GITHUB_STEP_SUMMARY
209+
else
210+
echo "✅ Successfully released version ${{ steps.get_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
211+
echo "" >> $GITHUB_STEP_SUMMARY
212+
echo "- Repository: ${{ inputs.repository }}" >> $GITHUB_STEP_SUMMARY
213+
echo "- Tag: v${{ steps.get_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
214+
if [ "${{ inputs.docker-enabled }}" == "true" ]; then
215+
echo "- Docker Image: ${{ secrets.ACR_URL }}/${{ inputs.docker-image-name }}:${{ steps.get_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
216+
fi
217+
fi
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Release Sandbox
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
dry_run:
7+
description: 'Dry run (skip release)'
8+
required: false
9+
type: boolean
10+
default: false
11+
12+
jobs:
13+
release:
14+
uses: GoPlasmatic/devops/.github/workflows/npm-release-reusable.yml@main
15+
with:
16+
repository: GoPlasmatic/Sandbox
17+
working-directory: website
18+
node-version: '20'
19+
dry-run: ${{ inputs.dry_run }}
20+
docker-enabled: true
21+
docker-image-name: sandbox
22+
secrets:
23+
GH_PAT: ${{ secrets.GH_PAT }}
24+
ACR_URL: ${{ secrets.ACR_URL }}
25+
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
26+
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}

0 commit comments

Comments
 (0)