diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml index 28ba5993..e7b78588 100644 --- a/.github/workflows/beta-release.yml +++ b/.github/workflows/beta-release.yml @@ -5,11 +5,7 @@ on: workflow_dispatch: jobs: - get-configs: - uses: ./.github/workflows/configs.yml - create-beta-tag: - needs: [get-configs] runs-on: ubuntu-latest permissions: contents: write diff --git a/.github/workflows/configs.yml b/.github/workflows/configs.yml index 85cdfda9..8c3bece8 100644 --- a/.github/workflows/configs.yml +++ b/.github/workflows/configs.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest env: NODE_VERSION: '22.x' - GO_VERSION: '1.25.x' + GO_VERSION: '1.25.4' outputs: node-version: ${{ env.NODE_VERSION }} go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff786e49..623b5ec0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,41 +58,113 @@ jobs: exit 1 fi - build-and-test: - needs: [ get-configs, version-and-tag ] - uses: ./.github/workflows/build-and-test.yml + build-and-test: + needs: [ get-configs, version-and-tag ] + uses: ./.github/workflows/build-and-test.yml + strategy: + fail-fast: true + matrix: + os: [ ubuntu-22.04, windows-latest, macos-latest ] + with: + ref: ${{ needs.version-and-tag.outputs.tag }} + runs-on: ${{ matrix.os }} + + bundle-linux: + needs: [ get-configs, version-and-tag, build-and-test ] strategy: fail-fast: true matrix: - os: [ ubuntu-22.04, windows-latest, macos-latest ] - with: - ref: ${{ needs.version-and-tag.outputs.tag }} - runs-on: ${{ matrix.os }} + include: + - { arch: "x64", docker-platform: "linux/amd64", go-arch: "amd64", use_qemu: false, node-version: "22" } + - { arch: "arm64", docker-platform: "linux/arm64", go-arch: "arm64", use_qemu: true, node-version: "22" } + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ needs.version-and-tag.outputs.tag }} + fetch-tags: true + + - name: Set release asset name + id: set-asset-name + shell: bash + run: | + APP_NAME=${{ needs.get-configs.outputs.app-name }} + VERSION=$(node -p "require('./package.json').version") + NODE_VERSION=${{ matrix.node-version }} + NODE_MAJOR=$(echo $NODE_VERSION | cut -d. -f1) + + TAG=${{ needs.version-and-tag.outputs.tag }} + if [[ "$TAG" =~ -alpha$ ]]; then + FILE_NAME="${APP_NAME}-${VERSION}-alpha-linux-${{ matrix.arch }}-node${NODE_MAJOR}" + elif [[ "$TAG" =~ -beta$ ]]; then + FILE_NAME="${APP_NAME}-${VERSION}-beta-linux-${{ matrix.arch }}-node${NODE_MAJOR}" + else + FILE_NAME="${APP_NAME}-${VERSION}-linux-${{ matrix.arch }}-node${NODE_MAJOR}" + fi + + ASSET_NAME=$(echo "$FILE_NAME" | tr '[:upper:]' '[:lower:]') + echo "ASSET_NAME=${ASSET_NAME}" >> $GITHUB_OUTPUT + + - name: Set up QEMU + if: matrix.use_qemu + uses: docker/setup-qemu-action@v3 + + - name: Bundle (${{ matrix.docker-platform }} + run: | + docker run --rm -v ${{ github.workspace }}:/work -w /work \ + --platform ${{ matrix.docker-platform }} \ + amazonlinux:2023 \ + /bin/bash -c ' + set -ex + yum install -y make gcc-c++ python3 tar gzip wget tree + + wget -q https://go.dev/dl/go${{ needs.get-configs.outputs.go-version }}.linux-${{ matrix.go-arch }}.tar.gz + tar -C /usr/local -xzf go${{ needs.get-configs.outputs.go-version }}.linux-${{ matrix.go-arch }}.tar.gz + export PATH=$PATH:/usr/local/go/bin + + curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash + export NVM_DIR="$HOME/.nvm" + . "$NVM_DIR/nvm.sh" + nvm install ${{ matrix.node-version }} + nvm use ${{ matrix.node-version }} + + npm ci - bundle: + TAG="${{ needs.version-and-tag.outputs.tag }}" + if [[ "$TAG" =~ -alpha$ ]]; then + npm run bundle:alpha + elif [[ "$TAG" =~ -beta$ ]]; then + npm run bundle:beta + else + npm run bundle:prod + fi + + tree -f bundle/production/node_modules/tree-sitter + tree -f bundle/production/node_modules/tree-sitter-json + + GOARCH=${{ matrix.go-arch }} go build -C ./cfn-init/cmd -v -o ../../bundle/production/bin/cfn-init + cp ./cfn-init/THIRD-PARTY-LICENSES.txt ./bundle/production/bin/ + ' + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.set-asset-name.outputs.ASSET_NAME }} + path: bundle/production/ + if-no-files-found: error + include-hidden-files: true + compression-level: 6 + + bundle-win-mac: needs: [ get-configs, version-and-tag, build-and-test ] strategy: fail-fast: true matrix: include: - - { os: "ubuntu-22.04", arch: "x64", platform: "linux", go-arch: "amd64", node-version: "18.x" } - - { os: "ubuntu-22.04", arch: "x64", platform: "linux", go-arch: "amd64", node-version: "20.x" } - - { os: "ubuntu-22.04", arch: "x64", platform: "linux", go-arch: "amd64", node-version: "22.x" } - - { os: "ubuntu-22.04", arch: "arm64", platform: "linux", go-arch: "arm64", node-version: "18.x" } - - { os: "ubuntu-22.04", arch: "arm64", platform: "linux", go-arch: "arm64", node-version: "20.x" } - - { os: "ubuntu-22.04", arch: "arm64", platform: "linux", go-arch: "arm64", node-version: "22.x" } - - { os: "ubuntu-22.04", arch: "arm", platform: "linux", go-arch: "arm", node-version: "18.x" } - - { os: "ubuntu-22.04", arch: "arm", platform: "linux", go-arch: "arm", node-version: "20.x" } - - { os: "ubuntu-22.04", arch: "arm", platform: "linux", go-arch: "arm", node-version: "22.x" } - - { os: "macos-latest", arch: "x64", platform: "darwin", go-arch: "amd64", node-version: "18.x" } - - { os: "macos-latest", arch: "x64", platform: "darwin", go-arch: "amd64", node-version: "20.x" } - { os: "macos-latest", arch: "x64", platform: "darwin", go-arch: "amd64", node-version: "22.x" } - - { os: "macos-latest", arch: "arm64", platform: "darwin", go-arch: "arm64", node-version: "18.x" } - - { os: "macos-latest", arch: "arm64", platform: "darwin", go-arch: "arm64", node-version: "20.x" } - { os: "macos-latest", arch: "arm64", platform: "darwin", go-arch: "arm64", node-version: "22.x" } - - { os: "windows-latest", arch: "x64", platform: "win32", go-arch: "amd64", node-version: "18.x" } - - { os: "windows-latest", arch: "x64", platform: "win32", go-arch: "amd64", node-version: "20.x" } - { os: "windows-latest", arch: "x64", platform: "win32", go-arch: "amd64", node-version: "22.x" } + - { os: "windows-11-arm", arch: "arm64", platform: "win32", go-arch: "arm64", node-version: "22.x" } runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 @@ -124,11 +196,11 @@ jobs: run: | TAG=${{ needs.version-and-tag.outputs.tag }} if [[ "$TAG" =~ -alpha$ ]]; then - npm run bundle:alpha -- --env platform=${{ matrix.platform }} --env arch=${{ matrix.arch }} + npm run bundle:alpha elif [[ "$TAG" =~ -beta$ ]]; then - npm run bundle:beta -- --env platform=${{ matrix.platform }} --env arch=${{ matrix.arch }} + npm run bundle:beta else - npm run bundle:prod -- --env platform=${{ matrix.platform }} --env arch=${{ matrix.arch }} + npm run bundle:prod fi - name: Bundle Go @@ -156,58 +228,56 @@ jobs: TAG=${{ needs.version-and-tag.outputs.tag }} if [[ "$TAG" =~ -alpha$ ]]; then - FILE_NAME="${APP_NAME}-${VERSION}-alpha-${PLATFORM}-${ARCH}-node${NODE_MAJOR}.zip" + FILE_NAME="${APP_NAME}-${VERSION}-alpha-${PLATFORM}-${ARCH}-node${NODE_MAJOR}" elif [[ "$TAG" =~ -beta$ ]]; then - FILE_NAME="${APP_NAME}-${VERSION}-beta-${PLATFORM}-${ARCH}-node${NODE_MAJOR}.zip" + FILE_NAME="${APP_NAME}-${VERSION}-beta-${PLATFORM}-${ARCH}-node${NODE_MAJOR}" else - FILE_NAME="${APP_NAME}-${VERSION}-${PLATFORM}-${ARCH}-node${NODE_MAJOR}.zip" + FILE_NAME="${APP_NAME}-${VERSION}-${PLATFORM}-${ARCH}-node${NODE_MAJOR}" fi ASSET_NAME=$(echo "$FILE_NAME" | tr '[:upper:]' '[:lower:]') echo "ASSET_NAME=${ASSET_NAME}" echo "ASSET_NAME=${ASSET_NAME}" >> $GITHUB_OUTPUT - - name: Create Zip (Unix) - if: runner.os != 'Windows' - env: - ASSET_NAME: ${{ steps.set-asset-name.outputs.ASSET_NAME }} - run: | - chmod -R 777 ./bundle/production - echo "Creating zip: ${{ env.ASSET_NAME }}" - (cd ./bundle/production && zip -r ../../${{ env.ASSET_NAME }} .) - - - name: Create Zip (Windows) - if: runner.os == 'Windows' - env: - ASSET_NAME: ${{ steps.set-asset-name.outputs.ASSET_NAME }} - run: | - icacls ./bundle/production /grant Everyone:F /T - echo "Creating zip: ${{ env.ASSET_NAME }}" - Compress-Archive -Path ./bundle/production/* -DestinationPath ./${{ env.ASSET_NAME }} - - name: Upload artifact uses: actions/upload-artifact@v4 with: name: ${{ steps.set-asset-name.outputs.ASSET_NAME }} - path: ${{ steps.set-asset-name.outputs.ASSET_NAME }} + path: bundle/production/ if-no-files-found: error + include-hidden-files: true + compression-level: 6 release: - needs: [ version-and-tag, bundle ] + needs: [ version-and-tag, bundle-linux, bundle-win-mac ] runs-on: ubuntu-latest permissions: contents: write steps: - name: Download all artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: path: artifacts + extract: false + + - name: Create zip files for release + run: | + ls -R artifacts/ + + cd artifacts + for dir in */; do + dirname="${dir%/}" + echo "Creating ${dirname}.zip" + (cd "$dirname" && zip -r "../${dirname}.zip" .) + rm -rf "$dirname" + done + ls -lh *.zip - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ needs.version-and-tag.outputs.tag }} prerelease: ${{ needs.version-and-tag.outputs.is-prerelease }} - files: artifacts/**/* + files: artifacts/*.zip generate_release_notes: true fail_on_unmatched_files: true diff --git a/package.json b/package.json index 72ddf733..a3c376ae 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,6 @@ "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@tree-sitter-grammars/tree-sitter-yaml", - "cbor-x", "cfn-guard", "lmdb", "pino", @@ -157,19 +156,22 @@ "tree-sitter-json", "vscode-languageserver-types" ], - "nativePrebuilds": { - "@lmdb/lmdb-darwin-arm64": "3.4.2", - "@lmdb/lmdb-darwin-x64": "3.4.2", - "@lmdb/lmdb-linux-arm": "3.4.2", - "@lmdb/lmdb-linux-arm64": "3.4.2", - "@lmdb/lmdb-linux-x64": "3.4.2", - "@lmdb/lmdb-win32-arm64": "3.4.2", - "@lmdb/lmdb-win32-x64": "3.4.2", - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" - } + "nativePrebuilds": [ + "@lmdb/lmdb-darwin-arm64", + "@lmdb/lmdb-darwin-x64", + "@lmdb/lmdb-linux-arm", + "@lmdb/lmdb-linux-arm64", + "@lmdb/lmdb-linux-x64", + "@lmdb/lmdb-win32-arm64", + "@lmdb/lmdb-win32-x64", + "@msgpackr-extract/msgpackr-extract-darwin-arm64", + "@msgpackr-extract/msgpackr-extract-darwin-x64", + "@msgpackr-extract/msgpackr-extract-linux-arm", + "@msgpackr-extract/msgpackr-extract-linux-arm64", + "@msgpackr-extract/msgpackr-extract-linux-x64", + "@msgpackr-extract/msgpackr-extract-win32-x64" + ], + "unusedDependencies": [ + "cbor-x" + ] } diff --git a/webpack.config.js b/webpack.config.js index 94d453be..f0ec442b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,9 +10,11 @@ const path = require('path'); const BUNDLE_NAME = 'cfn-lsp-server-standalone'; const Package = JSON.parse(fs.readFileSync('package.json', 'utf8')); const PackageLock = JSON.parse(fs.readFileSync('package-lock.json', 'utf8')); +const ExternalsDeps = Package.externalDependencies; +const NativePrebuilds = Package.nativePrebuilds; +const UnusedDeps = Package.unusedDependencies; const COPY_FILES = ['LICENSE', 'NOTICE', 'THIRD-PARTY-LICENSES.txt', 'README.md']; -const PLATFORMS = ['linux', 'win32', 'darwin']; const KEEP_FILES = [ '.cjs', '.gyp', @@ -28,7 +30,7 @@ const KEEP_FILES = [ const IGNORE_PATHS = ['/bin/', '/test/', '/benchmarks/', '/examples/']; function generateExternals() { - const externals = Package.externalDependencies; + const externals = [...ExternalsDeps, ...UnusedDeps]; const collected = new Set(externals); const queue = [...externals]; @@ -46,7 +48,7 @@ function generateExternals() { } } - for (const dep of Object.keys(Package.nativePrebuilds)) { + for (const dep of NativePrebuilds) { collected.add(dep); } return Array.from(collected).sort(); @@ -54,7 +56,7 @@ function generateExternals() { const EXTERNALS = generateExternals(); -function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, targetArch) { +function createPlugins(isDevelopment, outputPath, mode, env) { const plugins = []; plugins.push( @@ -90,7 +92,6 @@ function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, tar compiler.hooks.beforeRun.tapAsync('InstallDependencies', (compilation, callback) => { try { console.log('[InstallDependencies] Starting dependency installation...'); - console.log(`[InstallDependencies] Target: ${targetPlatform}-${targetArch}`); const tmpPkg = { ...Package, @@ -112,17 +113,20 @@ function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, tar console.log('[InstallDependencies] Running npm ci --omit=dev'); execSync('npm ci --omit=dev', { cwd: tmpDir, stdio: 'inherit' }); - const otherDeps = Object.entries(Package.nativePrebuilds) - .filter(([key, _version]) => { - return key.endsWith(`${targetPlatform}-${targetArch}`); - }) - .map(([key, version]) => { - return `${key}@${version}`; - }) - .join(' '); - console.log(`[InstallDependencies] Installing native prebuilds: ${JSON.stringify(otherDeps)}`); - execSync(`npm install --save-exact --force ${otherDeps}`, { cwd: tmpDir, stdio: 'inherit' }); + const externals = ExternalsDeps.map((dep) => { + if (dep === 'cfn-guard') { + return `${dep}@${PackageLock.packages[`node_modules/${dep}`].resolved}`; + } + return `${dep}@${PackageLock.packages[`node_modules/${dep}`].version}`; + }); + console.log( + `[InstallDependencies] Installing externals: ${JSON.stringify(externals, null, 2)}`, + ); + execSync(`npm install --save-exact ${externals.join(' ')}`, { + cwd: tmpDir, + stdio: 'inherit', + }); callback(); } catch (error) { console.error('[InstallDependencies] Error:', error); @@ -130,43 +134,6 @@ function createPlugins(isDevelopment, outputPath, mode, env, targetPlatform, tar } }); - compiler.hooks.afterEmit.tap('CleanUnusedNativeModules', () => { - console.log('[CleanUnusedNativeModules] Starting cleanup of unused native modules...'); - - const nodeModulesPath = path.join(outputPath, 'node_modules'); - - if (!fs.existsSync(nodeModulesPath)) { - console.log('[CleanUnusedNativeModules] No node_modules found, skipping cleanup'); - return; - } - - function cleanPlatformDirs(dir) { - if (!fs.existsSync(dir)) return; - - const entries = fs.readdirSync(dir, { withFileTypes: true }); - for (const entry of entries) { - if (!entry.isDirectory()) continue; - - const entryPath = path.join(dir, entry.name); - const isPlatformDir = PLATFORMS.some((p) => entry.name.includes(`${p}-`)); - const shouldKeep = entry.name.includes(`${targetPlatform}-${targetArch}`); - - if (isPlatformDir && !shouldKeep) { - console.log(`[CleanUnusedNativeModules] Deleted: ${entryPath}`); - fs.rmSync(entryPath, { recursive: true, force: true }); - } else if (entry.name === 'prebuilds') { - console.log(`[CleanUnusedNativeModules] Scanning prebuilds: ${entryPath}`); - cleanPlatformDirs(entryPath); - } else { - cleanPlatformDirs(entryPath); - } - } - } - - cleanPlatformDirs(nodeModulesPath); - console.log('[CleanUnusedNativeModules] Cleanup complete'); - }); - compiler.hooks.done.tap('CleanupTemp', () => { console.log('[CleanupTemp] Cleaning up temporary files...'); if (fs.existsSync(tmpDir)) { @@ -279,8 +246,6 @@ const baseConfig = { module.exports = (env = {}) => { const mode = env.mode; let awsEnv = env.env; - const targetPlatform = env.platform || process.platform; - const targetArch = env.arch || process.arch; // Validate mode const validModes = ['development', 'production']; @@ -306,8 +271,8 @@ module.exports = (env = {}) => { console.info(`Building server with mode: ${mode}`); console.info(`NODE_ENV: ${mode}`); console.info(`AWS_ENV: ${awsEnv}`); - console.info(`Platform: ${targetPlatform}`); - console.info(`Arch: ${targetArch}`); + console.info(`Platform: ${process.platform} Arch: ${process.arch}`); + console.info(`Node.js ${process.version} Versions: ${JSON.stringify(process.versions, null, 2)}`); console.info(`Output path: ${outputPath}`); return { @@ -334,6 +299,6 @@ module.exports = (env = {}) => { chunks: 'all', }, }, - plugins: createPlugins(isDevelopment, outputPath, mode, awsEnv, targetPlatform, targetArch), + plugins: createPlugins(isDevelopment, outputPath, mode, awsEnv), }; };