diff --git a/npm/npm-get-next-branched-version/action.yaml b/npm/npm-get-next-branched-version/action.yaml new file mode 100644 index 0000000..8579953 --- /dev/null +++ b/npm/npm-get-next-branched-version/action.yaml @@ -0,0 +1,59 @@ +name: npm-get-next-branched-version +description: "Get the next version of an NPM package based on the current branch status." +inputs: + name: + description: "The name of the package." + required: true + defaultVersion: + description: "Default version to use." + required: true + default: "1.0.0" + registry: + description: "NPM registry URL." + required: false + default: "https://npm.pkg.github.com" + authToken: + description: "Authentication token for the registry." + required: true + prerelease: + description: "Query prerelease packages. Values: true, false." + required: false + default: "${{ github.event_name == 'pull_request' }}" +outputs: + latestVersion: + description: "The current latest version for the package." + value: ${{ steps.npm.outputs.version || steps.npmFallback.outputs.version }} + nextVersion: + description: "The next version for the package." + value: ${{ steps.version.outputs.nextVersion }} +runs: + using: "composite" + steps: + - name: "Get the base version for the package." + uses: act/common/utils/version-increment-branch@master + id: base + with: + version: ${{ inputs.defaultVersion }} + - uses: act/common/npm/npm-query-versions-latest@master + id: npm + with: + name: ${{ inputs.name }} + registry: ${{ inputs.registry }} + authToken: ${{ inputs.authToken }} + filter: ^${{ steps.base.outputs.baseVersion }} + prerelease: ${{ inputs.prerelease }} + - name: "If no version is found, get the latest version for the package if we didn't already look for it." + uses: act/common/npm/npm-query-versions-latest@master + id: npmFallback + if: ${{ steps.npm.outputs.version == '' && inputs.prerelease != 'false' }} + with: + name: ${{ inputs.name }} + registry: ${{ inputs.registry }} + authToken: ${{ inputs.authToken }} + filter: ^${{ steps.base.outputs.majorMinorPatchVersion }} + prerelease: "false" + - name: "Get the next version for the package." + uses: act/common/utils/version-increment-branch@master + id: version + with: + version: ${{ steps.npm.outputs.version || steps.npmFallback.outputs.version || inputs.defaultVersion }} diff --git a/npm/npm-install/action.yaml b/npm/npm-install/action.yaml new file mode 100644 index 0000000..eb3455b --- /dev/null +++ b/npm/npm-install/action.yaml @@ -0,0 +1,36 @@ +name: npm-install +description: "Download the desired NPM package tarball." +inputs: + name: + description: "Name of the package." + required: true + version: + description: "Version of the package." + required: false + default: "" + prerelease: + description: "Install prerelease packages. Values: true, false." + required: false + default: "false" + outputDirectory: + description: "Directory for the output file." + required: true + default: "." + registry: + description: "NPM registry URL." + required: false + default: "https://npm.pkg.github.com" + authToken: + description: "Authentication token for the registry." + required: true +outputs: + tgz: + description: "The path to the .tgz file." + value: ${{ steps.download.outputs.tgz }} +runs: + using: "composite" + steps: + - name: "Download the package." + id: download + run: bash ${{ github.action_path }}/download_package.sh "${{ inputs.name }}" "${{ inputs.version }}" "${{ inputs.registry }}" "${{ inputs.authToken }}" "${{ inputs.outputDirectory }}" + shell: bash diff --git a/npm/npm-install/download_package.sh b/npm/npm-install/download_package.sh new file mode 100644 index 0000000..a98862a --- /dev/null +++ b/npm/npm-install/download_package.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -e + +PACKAGE_NAME="$1" +VERSION="$2" +REGISTRY="$3" +AUTH_TOKEN="$4" +OUTPUT_DIR="${5:-.}" + +if [[ ! -f "$GITHUB_OUTPUT" ]]; then + GITHUB_OUTPUT="/dev/fd/1" +fi + +# Ensure version is provided +if [[ -z "$VERSION" ]]; then + echo "Error: VERSION is required" + exit 1 +fi + +# Download the tarball +TARBALL_URL="$REGISTRY/$PACKAGE_NAME/-/$PACKAGE_NAME-$VERSION.tgz" +TGZ_NAME="$PACKAGE_NAME-$VERSION.tgz" +mkdir -p "$OUTPUT_DIR" +TGZ_PATH="$OUTPUT_DIR/$TGZ_NAME" + +echo "Downloading $TARBALL_URL" +CURL_HEADERS=() +if [[ -n "$AUTH_TOKEN" ]]; then + CURL_HEADERS+=(-H "Authorization: Bearer $AUTH_TOKEN") +fi + +# Download and capture HTTP status separately +# Temporarily disable 'exit on error' to capture status +set +e +HTTP_CODE=$(curl -sL -w "%{http_code}" -o "$TGZ_PATH" "${CURL_HEADERS[@]}" "$TARBALL_URL") +CURL_EXIT=$? +set -e + +# Check if curl command itself failed (network issues, DNS, etc) +if [[ $CURL_EXIT -ne 0 ]]; then + echo "Error: curl failed with exit code $CURL_EXIT (network error or invalid URL)" + rm -f "$TGZ_PATH" + exit 1 +fi + +# Check HTTP status code +if [[ "$HTTP_CODE" != "200" ]]; then + echo "Error: Failed to download package (HTTP $HTTP_CODE)" + if [[ -f "$TGZ_PATH" ]]; then + echo "Response preview:" + head -c 500 "$TGZ_PATH" + echo "" + rm -f "$TGZ_PATH" + fi + exit 1 +fi + +if [[ ! -f "$TGZ_PATH" ]]; then + echo "Error: Download failed - file not created" + exit 1 +fi + +echo "tgz=$TGZ_PATH" >> "$GITHUB_OUTPUT" + +echo "Downloaded $TGZ_NAME" diff --git a/npm/npm-push/action.yaml b/npm/npm-push/action.yaml new file mode 100644 index 0000000..ae72484 --- /dev/null +++ b/npm/npm-push/action.yaml @@ -0,0 +1,37 @@ +name: npm-push +description: "Publish an NPM package to a registry." +inputs: + tgz: + description: "Path to the tarball file to publish." + required: true + registry: + description: "NPM registry URL." + required: false + default: "https://npm.pkg.github.com" + apiKey: + description: "NPM authentication token." + required: true + nodeVersion: + description: "Node.js version to use." + required: false + default: "20" + access: + description: "Package access level: public or restricted." + required: false + default: "public" +runs: + using: "composite" + steps: + - name: "Setup Node.js" + uses: https://github.com/actions/setup-node@v4 + with: + node-version: ${{ inputs.nodeVersion }} + registry-url: ${{ inputs.registry }} + - name: "Publish package" + env: + NODE_AUTH_TOKEN: ${{ inputs.apiKey }} + run: | + echo "Publishing ${{ inputs.tgz }} to ${{ inputs.registry }}" + npm publish ${{ inputs.tgz }} --access ${{ inputs.access }} + echo "Package published successfully" + shell: bash diff --git a/npm/npm-query-versions-latest/action.yaml b/npm/npm-query-versions-latest/action.yaml new file mode 100644 index 0000000..d752809 --- /dev/null +++ b/npm/npm-query-versions-latest/action.yaml @@ -0,0 +1,46 @@ +name: npm-query-versions-latest +description: "Get the latest version of an NPM package." +inputs: + name: + description: "Name of the package." + required: true + registry: + description: "NPM registry URL." + required: false + default: "https://npm.pkg.github.com" + authToken: + description: "Authentication token for the registry." + required: true + prerelease: + description: "Include prerelease packages. Values: true, false." + required: false + default: "false" + filter: + description: "String to filter by." + required: false + filterIsExpression: + description: "Is the filter a Regular Expression? Values: true, false." + required: false + default: "false" +outputs: + version: + description: "The latest version of the specified package." + value: ${{ steps.parse.outputs.version }} +runs: + using: "composite" + steps: + - name: "Get versions." + id: versions + uses: act/common/npm/npm-query-versions@master + with: + name: ${{ inputs.name }} + registry: ${{ inputs.registry }} + authToken: ${{ inputs.authToken }} + prerelease: ${{ inputs.prerelease }} + - name: "Parse query result." + id: parse + uses: act/common/utils/get-latest-version@master + with: + versions: ${{ steps.versions.outputs.versions }} + filter: ${{ inputs.filter }} + filterIsExpression: ${{ inputs.filterIsExpression }} diff --git a/npm/npm-query-versions/action.yaml b/npm/npm-query-versions/action.yaml new file mode 100644 index 0000000..9ab90ba --- /dev/null +++ b/npm/npm-query-versions/action.yaml @@ -0,0 +1,28 @@ +name: npm-query-versions +description: "Get all versions of an NPM package from a registry." +inputs: + name: + description: "Name of the package." + required: true + registry: + description: "NPM registry URL." + required: false + default: "https://npm.pkg.github.com" + authToken: + description: "Authentication token for the registry." + required: true + prerelease: + description: "Include prerelease packages. Values: true, false." + required: false + default: "false" +outputs: + versions: + description: "Newline-separated list of versions." + value: ${{ steps.query.outputs.versions }} +runs: + using: "composite" + steps: + - name: "Query NPM registry for versions." + id: query + run: bash ${{ github.action_path }}/query_versions.sh "${{ inputs.name }}" "${{ inputs.registry }}" "${{ inputs.authToken }}" "${{ inputs.prerelease }}" + shell: bash diff --git a/npm/npm-query-versions/query_versions.sh b/npm/npm-query-versions/query_versions.sh new file mode 100644 index 0000000..75fa5fa --- /dev/null +++ b/npm/npm-query-versions/query_versions.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -e + +PACKAGE_NAME="$1" +REGISTRY="$2" +AUTH_TOKEN="$3" +PRERELEASE="$4" + +if [[ ! -f "$GITHUB_OUTPUT" ]]; then + GITHUB_OUTPUT="/dev/fd/1" +fi + +# Construct the registry URL +REGISTRY_URL="$REGISTRY" +if [[ "$REGISTRY" == "https://npm.pkg.github.com" ]]; then + # GitHub Packages uses a specific URL format + REGISTRY_URL="$REGISTRY" +fi + +# Build curl headers +CURL_HEADERS=(-H "Accept: application/vnd.npm.install-v1+json") +if [[ -n "$AUTH_TOKEN" ]]; then + CURL_HEADERS+=(-H "Authorization: Bearer $AUTH_TOKEN") +fi + +# Query the package metadata +RESPONSE=$(curl -sL "${CURL_HEADERS[@]}" "$REGISTRY_URL/$PACKAGE_NAME" 2>/dev/null || echo "{}") + +# Extract versions +if [[ "$PRERELEASE" == "true" ]]; then + # Get all versions + VERSIONS=$(echo "$RESPONSE" | jq -r '.versions | keys[]' 2>/dev/null || echo "") +else + # Filter out prerelease versions (those with - in them) + VERSIONS=$(echo "$RESPONSE" | jq -r '.versions | keys[] | select(contains("-") | not)' 2>/dev/null || echo "") +fi + +# Output versions separated by newlines +if [[ -n "$VERSIONS" ]]; then + echo "versions<> "$GITHUB_OUTPUT" + echo "$VERSIONS" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" +else + echo "versions=" >> "$GITHUB_OUTPUT" +fi diff --git a/npm/npm-test/action.yaml b/npm/npm-test/action.yaml new file mode 100644 index 0000000..1b9e92d --- /dev/null +++ b/npm/npm-test/action.yaml @@ -0,0 +1,69 @@ +name: npm-test +description: "Install dependencies and run tests for an NPM package." +inputs: + workingDirectory: + description: "Working directory containing package.json." + required: false + default: "." + nodeVersion: + description: "Node.js version to use." + required: false + default: "20" + testCommand: + description: "Command to run tests. Default: npm test" + required: false + default: "npm test" + coverageCommand: + description: "Command to run tests with coverage. Default: npm run test:coverage" + required: false + default: "npm run test:coverage" + runCoverage: + description: "Whether to run coverage. Values: true, false." + required: false + default: "false" + coverageDirectory: + description: "Directory for the coverage output. Default: coverage" + required: false + default: "coverage" + uploadArtifacts: + description: "Whether to upload test artifacts. Values: true, false." + required: false + default: "false" + artifactName: + description: "Name of the artifact to upload. Default: test-results" + required: false + default: "test-results" + retention-days: + description: "Number of retention days for artifacts. Default: 7" + required: false + default: "7" +runs: + using: "composite" + steps: + - name: "Setup Node.js" + uses: https://github.com/actions/setup-node@v4 + with: + node-version: ${{ inputs.nodeVersion }} + cache: npm + cache-dependency-path: ${{ inputs.workingDirectory }}/package-lock.json + - name: "Install dependencies" + run: npm ci + working-directory: ${{ inputs.workingDirectory }} + shell: bash + - name: "Run tests" + run: ${{ inputs.testCommand }} + working-directory: ${{ inputs.workingDirectory }} + shell: bash + - name: "Run tests with coverage" + if: ${{ inputs.runCoverage == 'true' }} + run: ${{ inputs.coverageCommand }} + working-directory: ${{ inputs.workingDirectory }} + shell: bash + - name: "Upload test artifacts" + if: ${{ inputs.uploadArtifacts == 'true' && always() }} + uses: https://github.com/ChristopherHX/gitea-upload-artifact@v4 + with: + name: ${{ inputs.artifactName }} + path: ${{ inputs.workingDirectory }}/${{ inputs.coverageDirectory }} + if-no-files-found: warn + retention-days: ${{ inputs.retention-days }}