diff --git a/.github/actions/publish-library-to-npm/action.yaml b/.github/actions/publish-library-to-npm/action.yaml index ac2a4f360b..dafd2867d9 100644 --- a/.github/actions/publish-library-to-npm/action.yaml +++ b/.github/actions/publish-library-to-npm/action.yaml @@ -5,25 +5,31 @@ inputs: secret-token: description: "NPM auth token" required: true + release: description: "Use tag. Release stream, temporary here for compatibility with old workflows" required: false + tag: description: "Release Stream (npm dist-tag)" required: false + path: - description: "Path to artifact to publish" + description: "[DEPRECATED] Directory containing package.json (legacy); use workdir" required: false + + workdir: + description: "Directory containing package.json (preferred for monorepo)" + required: false + git-token: description: "PAT with contents:write to create and push git tags" required: false + create-tag: description: "Set to 'true' to create a git tag v after successful npm release" required: false default: "false" - workdir: - default: "." - type: string runs: using: composite @@ -34,124 +40,150 @@ runs: registry-url: "https://registry.npmjs.org/" scope: "@qvac" - - run: | - echo registry=https://registry.npmjs.org/ >> .npmrc - echo @qvac:registry=https://registry.npmjs.org/ >> .npmrc - echo //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN} >> .npmrc - shell: bash - env: - NODE_AUTH_TOKEN: ${{ inputs.secret-token }} - - name: Publish to NPM and optionally tag repo shell: bash env: NODE_AUTH_TOKEN: ${{ inputs.secret-token }} - INPUTS_CONTEXT: ${{ toJSON(inputs) }} GIT_PUSH_TOKEN: ${{ inputs.git-token }} CREATE_TAG: ${{ inputs.create-tag }} + INPUT_PATH: ${{ inputs.path }} + INPUT_WORKDIR: ${{ inputs.workdir }} + INPUT_TAG: ${{ inputs.tag }} + INPUT_RELEASE: ${{ inputs.release }} + EVENT_NAME: ${{ github.event_name }} + GIT_REF: ${{ github.ref }} + GIT_REPO: ${{ github.repository }} run: | - echo "publishing to npm" - # run: | - # set -x - - # if [ -n "${{ inputs.path }}" ]; then - # echo "Changing directory to ${{ inputs.path }}" - # cd "${{ inputs.path }}" - # fi - - # ### Get tag for NPM, support release for compatibility with old workflows - # echo "Input context: $INPUTS_CONTEXT" - - # TAG="${{ inputs.tag }}" - # RELEASE="${{ inputs.release }}" - - # if [ -z "$RELEASE" ]; then - # RELEASE="tmp" - # fi - # if [ -z "$TAG" ]; then - # TAG=$RELEASE - # fi - - # ### Set tag to latest if PR was merged to main - # echo "Base branch: ${{ github.ref }}" - # echo "Event name: ${{ github.event_name }}" - - # if [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/main" ]; then - # echo "PR was merged to main, setting tag to latest" - # TAG="latest" - # fi - - # ### Do NOT publish dev/tmp builds to NPM - # if [ "$TAG" = "dev" ] || [ "$TAG" = "tmp" ]; then - # echo "Skipping publish to NPM for non-release tag: $TAG" - # exit 0 - # fi - - # ### Use the package's existing version for release publishes - # SHORT_SHA=$(git rev-parse --short HEAD || echo "unknown") - # PACKAGE_VERSION=$(node -p "require('./package.json').version") - # PACKAGE_NAME=$(node -p "require('./package.json').name") - # VERSION="${PACKAGE_VERSION}" - - # echo "Publishing package: $PACKAGE_NAME" - # echo "Publishing with tag: $TAG" - # echo "Publishing version: $VERSION" - - # ### Set access level - private by default until release - # ACCESS="restricted" - - # ### Check if version already exists on npm - # if npm view "$PACKAGE_NAME@$VERSION" version >/dev/null 2>&1; then - # echo "Version $VERSION already exists for $PACKAGE_NAME on npm, skipping publish" - # exit 0 - # fi - - # ### Publish with provenance if supported and access is public - # if ([ "${{ github.event_name }}" = "push" ] || [ "${{ github.event_name }}" = "workflow_dispatch" ]) && [ "$ACCESS" = "public" ]; then - # echo "Publishing with provenance statements" - # npm publish --tag "$TAG" --access "$ACCESS" --provenance - # else - # npm publish --tag "$TAG" --access "$ACCESS" - # fi - - # echo "npm publish completed successfully" - - # #################################################################### - # # Optional: create and push git tag v - # #################################################################### - - # if [ "${CREATE_TAG}" != "true" ]; then - # echo "Tag creation disabled (create-tag != 'true'), skipping git tag step" - # exit 0 - # fi - - # if [ -z "${GIT_PUSH_TOKEN:-}" ]; then - # echo "create-tag is 'true' but no git-token provided, skipping git tag step" - # exit 0 - # fi - - # TAG_NAME="v${VERSION}" - - # echo "Preparing to create git tag: ${TAG_NAME}" - - # # Configure git identity if not already set - # git config user.name >/dev/null 2>&1 || git config user.name "github-actions[bot]" - # git config user.email >/dev/null 2>&1 || git config user.email "github-actions[bot]@users.noreply.github.com" - - # # Check if tag already exists locally - # if git rev-parse "${TAG_NAME}" >/dev/null 2>&1; then - # echo "Tag ${TAG_NAME} already exists locally" - # else - # git tag -a "${TAG_NAME}" -m "Release ${PACKAGE_NAME} ${TAG_NAME}" - # echo "Created local tag ${TAG_NAME}" - # fi - - # # Check if tag already exists on remote - # if git ls-remote --tags origin "${TAG_NAME}" | grep -q "${TAG_NAME}"; then - # echo "Tag ${TAG_NAME} already exists on remote origin, skipping push" - # exit 0 - # fi - - # echo "Pushing tag ${TAG_NAME} to origin" - # git push "https://x-access-token:${GIT_PUSH_TOKEN}@github.com/${{ github.repository }}.git" "${TAG_NAME}" - # echo "Tag ${TAG_NAME} pushed successfully" + set -x + set -euo pipefail + + ROOT="${GITHUB_WORKSPACE:-$(pwd)}" + + #################################################################### + # Resolve target directory (workdir preferred; path supported legacy) + #################################################################### + TARGET_DIR="" + + if [ -n "${INPUT_PATH}" ]; then + TARGET_DIR="${INPUT_PATH}" + echo "::warning title=Deprecated input::inputs.path is deprecated; use inputs.workdir instead." + elif [ -n "${INPUT_WORKDIR}" ]; then + TARGET_DIR="${INPUT_WORKDIR}" + else + echo "ERROR: You must set inputs.workdir (preferred) or inputs.path (deprecated legacy)." >&2 + exit 1 + fi + + echo "Target directory: ${TARGET_DIR}" + + if [ ! -d "${TARGET_DIR}" ]; then + echo "ERROR: Target directory does not exist: ${TARGET_DIR}" >&2 + exit 1 + fi + + cd "${TARGET_DIR}" + + if [ ! -f "package.json" ]; then + echo "ERROR: package.json not found in target directory: ${TARGET_DIR}" >&2 + exit 1 + fi + + #################################################################### + # Write .npmrc + #################################################################### + echo "Writing .npmrc in $(pwd)" + { + echo "registry=https://registry.npmjs.org/" + echo "@qvac:registry=https://registry.npmjs.org/" + echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" + } > .npmrc + + #################################################################### + # Core logic preserved below + #################################################################### + + TAG="${INPUT_TAG}" + RELEASE="${INPUT_RELEASE}" + + if [ -z "$RELEASE" ]; then + RELEASE="tmp" + fi + if [ -z "$TAG" ]; then + TAG=$RELEASE + fi + + echo "Base branch: ${GIT_REF}" + echo "Event name: ${EVENT_NAME}" + + if [ "${EVENT_NAME}" = "push" ] && [ "${GIT_REF}" = "refs/heads/main" ]; then + echo "PR was merged to main, setting tag to latest" + TAG="latest" + fi + + if [ "$TAG" = "dev" ] || [ "$TAG" = "tmp" ]; then + echo "Skipping publish to NPM for non-release tag: $TAG" + exit 0 + fi + + SHORT_SHA=$(git -C "$ROOT" rev-parse --short HEAD || echo "unknown") + PACKAGE_VERSION=$(node -p "require('./package.json').version") + PACKAGE_NAME=$(node -p "require('./package.json').name") + VERSION="${PACKAGE_VERSION}" + + echo "Publishing package: $PACKAGE_NAME" + echo "Publishing with tag: $TAG" + echo "Publishing version: $VERSION" + echo "Commit: $SHORT_SHA" + + ACCESS="restricted" + + if npm view "$PACKAGE_NAME@$VERSION" version >/dev/null 2>&1; then + echo "Version $VERSION already exists for $PACKAGE_NAME on npm, skipping publish" + exit 0 + fi + + if ([ "${EVENT_NAME}" = "push" ] || [ "${EVENT_NAME}" = "workflow_dispatch" ]) && [ "$ACCESS" = "public" ]; then + echo "Publishing with provenance statements" + npm publish --tag "$TAG" --access "$ACCESS" --provenance + else + npm publish --tag "$TAG" --access "$ACCESS" + fi + + echo "npm publish completed successfully" + + #################################################################### + # Optional: create and push git tag v + #################################################################### + + if [ "${CREATE_TAG}" != "true" ]; then + echo "Tag creation disabled (create-tag != 'true'), skipping git tag step" + exit 0 + fi + + if [ -z "${GIT_PUSH_TOKEN:-}" ]; then + echo "create-tag is 'true' but no git-token provided, skipping git tag step" + exit 0 + fi + + TAG_NAME="v${VERSION}" + + echo "Preparing to create git tag: ${TAG_NAME}" + + git -C "$ROOT" config user.name >/dev/null 2>&1 || git -C "$ROOT" config user.name "github-actions[bot]" + git -C "$ROOT" config user.email >/dev/null 2>&1 || git -C "$ROOT" config user.email "github-actions[bot]@users.noreply.github.com" + + if git -C "$ROOT" rev-parse "${TAG_NAME}" >/dev/null 2>&1; then + echo "Tag ${TAG_NAME} already exists locally" + else + git -C "$ROOT" tag -a "${TAG_NAME}" -m "Release ${PACKAGE_NAME} ${TAG_NAME}" + echo "Created local tag ${TAG_NAME}" + fi + + if git -C "$ROOT" ls-remote --tags origin "${TAG_NAME}" | grep -q "${TAG_NAME}"; then + echo "Tag ${TAG_NAME} already exists on remote origin, skipping push" + exit 0 + fi + + echo "Pushing tag ${TAG_NAME} to origin" + git -C "$ROOT" push "https://x-access-token:${GIT_PUSH_TOKEN}@github.com/${GIT_REPO}.git" "${TAG_NAME}" + echo "Tag ${TAG_NAME} pushed successfully" \ No newline at end of file