Release & Publish to NPM #16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release & Publish to NPM | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version to publish (e.g., 1.0.0)" | |
| required: true | |
| type: string | |
| release: | |
| types: [published] | |
| # Empêche les publications en double si tag + release se déclenchent | |
| concurrency: | |
| group: publish-${{ github.ref || github.event.release.tag_name || github.event.inputs.version }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| id-token: write | |
| env: | |
| NODE_VERSION: "22" | |
| CI: "true" | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| test-before-publish: | |
| name: Tests & Quality Check | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Verify submodules | |
| run: | | |
| echo "Checking submodule status..." | |
| git submodule status | |
| echo "Listing external directory..." | |
| ls -la external/ || true | |
| if [ ! -f "external/soem/CMakeLists.txt" ]; then | |
| echo "SOEM submodule not properly initialized, initializing..." | |
| git submodule update --init --recursive --force | |
| ls -la external/soem/ || true | |
| fi | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| check-latest: true | |
| cache: "npm" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Lint check | |
| run: npm run lint:check | |
| # Option A: rendre non-bloquant (souvent préférable) | |
| - name: Security audit | |
| run: npm audit --audit-level=high | |
| continue-on-error: true | |
| - name: Run tests with coverage | |
| run: npm run test:ci | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| file: ./coverage/lcov.info | |
| flags: unittests | |
| name: codecov-umbrella | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: true | |
| build-binaries: | |
| name: Build on ${{ matrix.os }} | |
| needs: test-before-publish | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] # ajoute macos-latest si besoin | |
| include: | |
| - os: ubuntu-latest | |
| platform: linux | |
| - os: windows-latest | |
| platform: win32 | |
| # - os: macos-latest | |
| # platform: darwin | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Verify submodules | |
| shell: bash | |
| run: | | |
| echo "Checking submodule status..." | |
| git submodule status | |
| echo "Listing external directory..." | |
| ls -la external/ || mkdir -p external | |
| if [ ! -f "external/soem/CMakeLists.txt" ]; then | |
| echo "SOEM submodule not properly initialized, trying multiple methods..." | |
| git submodule deinit -f external/soem || true | |
| rm -rf external/soem | |
| git submodule update --init --recursive --force || echo "Method 1 failed" | |
| if [ ! -f "external/soem/CMakeLists.txt" ]; then | |
| echo "Direct clone fallback..." | |
| rm -rf external/soem | |
| git clone --depth=1 --branch=master https://github.com/OpenEtherCATsociety/SOEM.git external/soem | |
| fi | |
| if [ ! -f "external/soem/CMakeLists.txt" ]; then | |
| echo "ERROR: Could not initialize SOEM submodule properly" | |
| ls -la external/soem/ || echo "Directory doesn't exist" | |
| exit 1 | |
| fi | |
| echo "SOEM successfully initialized:" | |
| ls -la external/soem/ | |
| else | |
| echo "SOEM submodule already properly initialized" | |
| fi | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| check-latest: true | |
| cache: "npm" | |
| - name: Install system dependencies (Linux) | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libpcap-dev cmake build-essential | |
| - name: Install system dependencies (Windows) | |
| if: matrix.os == 'windows-latest' | |
| shell: powershell | |
| run: | | |
| Write-Host "Windows runner already has toolchains; proceeding." | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build native addon | |
| run: npm run build | |
| - name: Test binary & outputs | |
| shell: bash | |
| run: | | |
| echo "Testing binary on ${{ matrix.platform }}..." | |
| test -f build/Release/soem_addon.node || (ls -la build/Release/ || true; exit 1) | |
| test -f dist/index.js || (ls -la dist/ || true; exit 1) | |
| if [ -f types/index.d.ts ]; then echo "✓ TypeScript definitions present"; else echo "✗ Missing types"; exit 1; fi | |
| node ./scripts/ci-test-module.js || true | |
| # ⚠️ Ces artefacts ne sont utiles que si tu les publies/re-distribues (prebuilds) | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-${{ matrix.platform }} | |
| path: | | |
| build/Release/soem_addon.node | |
| dist/ | |
| types/ | |
| retention-days: 1 | |
| publish-npm: | |
| name: Publish to NPM | |
| needs: [test-before-publish, build-binaries] | |
| runs-on: ubuntu-latest | |
| # Publie seulement sur tag v* ou via dispatch | |
| if: > | |
| (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || | |
| (github.event_name == 'workflow_dispatch') || | |
| (github.event_name == 'release' && github.event.action == 'published') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Verify submodules | |
| run: | | |
| git submodule update --init --recursive --force | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| check-latest: true | |
| registry-url: "https://registry.npmjs.org" | |
| cache: "npm" | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libpcap-dev cmake build-essential | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Resolve version to publish | |
| id: get_version | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| VERSION="${{ github.event.inputs.version }}" | |
| elif [[ "${{ github.event_name }}" == "release" ]]; then | |
| VERSION="${{ github.event.release.tag_name }}" | |
| VERSION="${VERSION#v}" | |
| else | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Publishing version: $VERSION" | |
| - name: Update package.json version | |
| id: set_version | |
| env: | |
| VERSION: ${{ steps.get_version.outputs.version }} | |
| run: | | |
| echo "Setting package.json version to $VERSION" | |
| node -e "const fs=require('fs');const v=process.env.VERSION;const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.version=v;fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n');console.log('package.json version set to',p.version)" | |
| - name: Verify package.json version changed | |
| run: | | |
| NEW=$(node -e "console.log(require('./package.json').version)") | |
| echo "package.json version is $NEW" | |
| if [ "$NEW" = "${{ steps.get_version.outputs.version }}" ]; then | |
| echo "Version correctly set" | |
| else | |
| echo "Version mismatch: expected ${{ steps.get_version.outputs.version }}, got $NEW" | |
| exit 1 | |
| fi | |
| - name: Commit package.json and create tag (workflow_dispatch only) | |
| if: github.event_name == 'workflow_dispatch' | |
| env: | |
| VERSION: ${{ steps.get_version.outputs.version }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo "Committing package.json and creating tag v$VERSION" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add package.json | |
| # Commit if there are changes | |
| if git diff --staged --quiet; then | |
| echo "No changes to commit" | |
| else | |
| git commit -m "chore(release): v$VERSION" | |
| # Push the commit to the current branch | |
| git push origin HEAD | |
| fi | |
| # Create annotated tag if it does not already exist | |
| git fetch --tags | |
| if git rev-parse "v$VERSION" >/dev/null 2>&1; then | |
| echo "Tag v$VERSION already exists, skipping tag creation" | |
| else | |
| git tag -a "v$VERSION" -m "Release v$VERSION" | |
| git push origin "v$VERSION" | |
| fi | |
| - name: Build for publication | |
| run: npm run build | |
| - name: Run final tests | |
| run: npm test | |
| - name: Publish to NPM (with provenance) | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| # --access public si besoin (package public ou scope) | |
| npm publish --provenance --access public | |
| - name: Create GitHub Release if needed | |
| if: github.event_name != 'release' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ steps.get_version.outputs.version }}" | |
| gh release create "v${VERSION}" \ | |
| --title "Release v${VERSION}" \ | |
| --notes "## 🚀 Release v${VERSION} | |
| ### 📦 NPM Package | |
| \`\`\`bash | |
| npm install soem-node@${VERSION} | |
| \`\`\` | |
| ### 🔗 Links | |
| - NPM : https://www.npmjs.com/package/soem-node/v/${VERSION} | |
| - Repo : https://github.com/MXASoundNDEv/SOEM-Nodejs | |
| - Docs : https://github.com/MXASoundNDEv/SOEM-Nodejs#readme | |
| ### 📋 Changes | |
| Voir [CHANGELOG.md](https://github.com/MXASoundNDEv/SOEM-Nodejs/blob/main/CHANGELOG.md)." | |
| notify-success: | |
| name: Notify Success | |
| needs: [publish-npm] | |
| runs-on: ubuntu-latest | |
| if: success() | |
| steps: | |
| - name: Success notification | |
| run: | | |
| echo "🎉 Publication réussie !" | |
| echo "📦 Package publié sur NPM" | |
| echo "🔗 NPM: https://www.npmjs.com/package/soem-node" |