Skip to content

Nuxt.js Project GitHub Actions Automated CI/CD Practices (Part 1): Build and Release ​

Nuxt.js Project GitHub Actions Automated CI/CD Practice (Part 1): Build and Release Diagram

Preface ​

In modern front-end development workflows, automated CI/CD has become standard. However, many developers find it tedious to manually perform the following operations:

  • Every release requires manually performing the build and packaging.

  • Build artifacts need to be manually uploaded to Release

  • Deploying to the server requires manually copying files and restarting the service.

  • Unable to perceive the execution status of CI/CD

To solve these pain points, this series of articles will guide you from scratch through building a complete automated CI/CD pipeline for Nuxt.js projects using GitHub Actions.

This series of articles is divided into three parts:

PartGoalExpected results
Part One (Main Text)Implement automatic build, packaging, and upload to GitHub Release when a Release is created.After creating a Release, automatically generate tar.gz/zip attachments and automatically update the package.json version number.
Part TwoImplement automatic deployment to a remote serverAutomatically transfer the build artifacts to the server, restart the service via PM2, and complete deployment.
Part ThreeImplement CI/CD email notificationsSend an email notification after a successful build and deployment, and issue a timely alert on failure.

Effect achieved:

When you create a new Release on GitHub, the entire process will run fully automatically:

  • Automatic code checkout → dependency installation → code inspection → project build

  • Generate a compressed package and upload it to the Release page.

  • Build artifacts are automatically transferred to the server

  • PM2 automatic restart service

  • Eventually you will receive an email containing the deployment results.

NOTICE

This article is The first part of the article series, focusing on the build and release phases.

Part One Objectives ​

When you create a new Release on GitHub, automatically:

  • Code checkout and dependency installation

  • Code check (Lint + Typecheck)

  • Nuxt.js project build

  • Generate archive (tar.gz + zip)

  • Upload to GitHub Release

  • Save build artifacts for subsequent deployment use

  • Automatically commit version number updates

Directory structure ​

.github/
└── workflows/
    └── release-bump.yaml

Complete configuration file ​

yaml
name: Auto Bump Version on Release

on:
  release:
    types: [created]  # Triggered only when a new Release is created

jobs:
  # Job 1: Build and Release
  build-and-release:
    runs-on: ubuntu-latest
    permissions:
      contents: write  # Allow pushing code and creating Releases
    outputs:
      version: ${{ steps.extract-version.outputs.version }}
      project_name: ${{ steps.project-name.outputs.project_name }} # Passed to the subsequent deployment Job

    steps:
      # 1. Check out the code
      - uses: actions/checkout@v6
        with:
          ref: ${{ github.event.release.target_commitish }}
          fetch-depth: 0

      # 2. Install pnpm
      - name: Install pnpm
        uses: pnpm/action-setup@v4

      # 3. Set up Node.js
      - name: Setup Node.js
        uses: actions/setup-node@v5
        with:
          node-version: 20
          cache: 'pnpm'

      # 4. Install dependencies
      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      # 5. Code inspection
      - name: Lint
        run: pnpm run lint

      # 6. Type checking
      - name: Typecheck
        run: pnpm run typecheck

      # 7. Extract the version number
      - name: Extract Version
        id: extract-version
        run: |
          TAG=${GITHUB_REF#refs/tags/}
          VERSION=$(echo "$TAG" | sed 's/^v//')
          echo "version=$VERSION" >> $GITHUB_OUTPUT

      # 8. Extract project name (dynamically obtained from package.json)
      - name: Extract Project Name
        id: project-name
        run: |
          PROJECT_NAME=$(node -p "require('./package.json').name")
          echo "project_name=$PROJECT_NAME" >> $GITHUB_OUTPUT

      # 9. Update the package.json version number
      - name: Update Version
        run: |
          pnpm version ${{ steps.extract-version.outputs.version }} --no-git-tag-version
          cat package.json | grep version

      # 10. Build a Nuxt.js project
      - name: Build Nuxt.js
        env:
          NODE_OPTIONS: "--max-old-space-size=4096"
        run: pnpm run build

      # 11. Create a compressed archive (using a dynamic project name)
      - name: Create Distribution Archive
        run: |
          if [ ! -d ".output" ]; then
            echo "Error: .output directory not found!"
            exit 1
          fi

          echo "Contents of .output directory:"
          ls -la .output/

          PROJECT_NAME="${{ steps.project-name.outputs.project_name }}"
          VERSION="${{ steps.extract-version.outputs.version }}"

          tar -czf ${PROJECT_NAME}-${VERSION}.tar.gz -C .output .
          cd .output && zip -r ../${PROJECT_NAME}-${VERSION}.zip . && cd ..

          echo "Archive created successfully!"
          ls -la *.tar.gz *.zip

      # 12. Upload to GitHub Release
      - name: Upload Release Assets
        uses: ncipollo/release-action@v1
        with:
          artifacts: "./${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gz,./${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.zip"
          allowUpdates: true
          omitBody: true
          omitName: true
          token: ${{ secrets.ACCESS_TOKEN }}
          makeLatest: "true"  # Uploading assets will not break the Release's latest property.

      # 13. Upload build artifacts for use by subsequent jobs
      - name: Upload Build Artifact
        uses: actions/upload-artifact@v7
        with:
          name: build-output
          path: ${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gz
          retention-days: 1
          if-no-files-found: error
          overwrite: true

      # 14. Submit version number change
      - name: Commit Changes
        run: |
          git config user.name "GitHub Actions"
          git config user.email "[email protected]"
          git add package.json pnpm-lock.yaml
          git commit -m "chore(release): bump to ${{ steps.extract-version.outputs.version }} [skip ci]"
          git push origin HEAD:${{ github.event.release.target_commitish }}

Required Secrets configuration ​

In the GitHub repository's Settings -> Secrets and variables -> Actions Add to:

Secret nameDescription
ACCESS_TOKENPersonal Access Token with write permission (used to upload Release assets)

💡 Note

default GITHUB_TOKEN In some scenarios, the permissions are insufficient. It is recommended to use a Fine-grained Personal Access Token and grant contents: write and actions: write Permission.

Detailed Explanation of Key Steps ​

1. Trigger condition ​

yaml
on:
  release:
    types: [created]

Trigger only when the user manually creates a Release, avoiding triggering a build on every push.

2. Version number extraction ​

yaml
TAG=${GITHUB_REF#refs/tags/}
VERSION=$(echo "$TAG" | sed 's/^v//')

From the Release tag (e.g.v1.0.0)extract the pure version number from(1.0.0).

3. Project Name Extraction ​

yaml
PROJECT_NAME=$(node -p "require('./package.json').name")

From package.json Dynamically read the project name to avoid hardcoding in configuration files, improving the generality and maintainability of the configuration.

💡Extended explanation: extracted project_name will pass through Job's outputs The field is passed to the subsequent deployment Job (Part 2 content). This allows the project name to be dynamically obtained during the deployment phase and used to build the deployment path and file name on the server, enabling data sharing and decoupling between Jobs.

4. Version number update ​

yaml
pnpm version ${{ steps.extract-version.outputs.version }} --no-git-tag-version

Update package.json in the version field, but do not create a git tag (the tag has already been created by Release).

5. Build Optimization ​

yaml
env:
  NODE_OPTIONS: "--max-old-space-size=4096"

Allocate 4GB of memory to Node.js to avoid out-of-memory errors when building large projects.

6. Archive creation ​

yaml
PROJECT_NAME="${{ steps.project-name.outputs.project_name }}"
VERSION="${{ steps.extract-version.outputs.version }}"
tar -czf ${PROJECT_NAME}-${VERSION}.tar.gz -C .output .

Packaging .output of the directory content, rather than the directory itself. This way, after extraction you get the files directly, with no need to handle the directory hierarchy additionally.

7. Release Asset Upload and Latest Tagging ​

yaml
- name: Upload Release Assets
  uses: ncipollo/release-action@v1
  with:
    artifacts: "./${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gz,./${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.zip"
    allowUpdates: true
    omitBody: true
    omitName: true
    token: ${{ secrets.ACCESS_TOKEN }}
    makeLatest: "true"  # Keep the latest tag for Release

Parameter description:

ParameterDescription
artifactsAsset file path to upload; separate multiple files with commas.
allowUpdatesAllow overwriting existing assets with the same name
omitBodyDo not update the Release description content
omitNameDo not update the Release name
tokenGitHub Personal Access Token
makeLatestControl the latest tag behavior of Release

makeLatest Available values:

valueDescription
"true"Mark this Release as latest (recommended)
"false"Do not mark as latest
"legacy"Use GitHub's legacy latest logic

⚠️Precautions: If not explicitly set makeLatest: "true", uploading assets may accidentally break the Release's latest attribute. It is recommended to always add this parameter to ensure that the latest version is correctly marked.

8. Build Artifact Preservation ​

yaml
- name: Upload Build Artifact
  uses: actions/upload-artifact@v7
  with:
    name: build-output
    path: ${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gz

Save the tar.gz file as an artifact for use by subsequent deployment Jobs. Use overwrite: true Avoid name conflicts.

💡 Pitfall Alert

upload-artifact@v4 There is a bug when uploading a directory; suggestions:Upload a file (such as tar.gz) instead of a directory.

Operation process ​

  • Create a new Release in the GitHub repository, fill in the version number (e.g.v0.4.0-alpha11)

  • GitHub Actions automatically trigger workflow

  • Execute checkout → Install dependencies → Check → Build → Package → Upload

  • Automatically update the version number and push to the repository.

  • Downloadable items appear on the Release page tar.gz and zip Attachment

Frequently Asked Questions ​

Q:upload-artifact Error "No files found"? ​

A: Use version v7 and upload.File rather than a directory:

yaml
path: ${{ steps.project-name.outputs.project_name }}-${{ steps.extract-version.outputs.version }}.tar.gz

Q: Why is it needed ACCESS_TOKEN? ​

A: Default GITHUB_TOKEN In some permission scenarios, you cannot upload Release assets, so using a Personal Access Token is more reliable.

Q:fetch-depth: 0 What is the function of? ​

A: Get the complete git history to ensure the version number commit can be pushed correctly.

Summary ​

This article completes the first part of the series—automation of building and publishing. By configuring GitHub Actions, we have achieved:

  • Automatically trigger a build when a Release is created

  • Automatically generate tar.gz and zip archives

  • Automatically upload to the Release page

  • Automatically update the version number in package.json

💡 Confirmation Point

After running this workflow, the Release page should have tar.gz and zip attachments, and in the repository package.json The version number has been updated.

Series navigation ​

Next article:Nuxt.js Project GitHub Actions Automated CI/CD Practice (Part 2): Remote Deployment- Will explain how to automatically deploy build artifacts to a remote server, covering SCP transfer, SSH command execution, PM2 service restart, and more.

Last updated on11/07/2026, 02:16 PM
@fishfinal | @fishfinals | [email protected]