Skip to content

Commit

Permalink
feat(CICD): #27805 Adding NPM publishing to nightly deployment workfl…
Browse files Browse the repository at this point in the history
…ow. (#28248)

* #27805 Adding NPM publishing to nightly deployment workflow.

* #27805 Fixing the path of the download artifact step.

* #27805 Refactor on publish npm cli workflow.

* #27805 Fixing trigger conditional for publishing NPM package.

* #27805 Find a previous build if the current is missing.

---------

Co-authored-by: Daniel Colina <[email protected]>
  • Loading branch information
dcolina and dcolina authored Apr 23, 2024
1 parent 239b434 commit 4643ce6
Show file tree
Hide file tree
Showing 5 changed files with 385 additions and 76 deletions.
85 changes: 85 additions & 0 deletions .github/actions/publish-npm-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# CLI Publish NPM Package Action

## Description

This GitHub Action workflow packages the dotCMS CLI as an NPM project.

## Inputs

### `ref`

Branch to build from.

- **Description**: Branch to build the project from.
- **Required**: No
- **Default**: `master`

### `github-token`

GitHub Token.

- **Description**: GitHub Token required to access GitHub resources.
- **Required**: Yes

### `npm-token`

NPM Token.

- **Description**: NPM Token required to publish the package to the NPM registry.
- **Required**: Yes

### `npm-package-name`

NPM package name.

- **Description**: NPM package name to be published.
- **Required**: No
- **Default**: `dotcli`

### `npm-package-scope`

NPM package scope.

- **Description**: NPM package scope.
- **Required**: No
- **Default**: `@dotcms`

### `node-version`

Node.js version.

- **Description**: Node.js version to be used to build the project.
- **Required**: No
- **Default**: `19`

### `workflow-to-search`

Workflow to search for artifacts.

- **Description**: Name of the workflow to search for to get the artifacts.
- **Required**: No
- **Default**: `build-test-master.yml`

### `artifact-id`

Artifact id.

- **Description**: Identifier of the artifact to be searched for and downloaded.
- **Required**: No
- **Default**: `cli-artifacts-*`

## Usage

```yaml
- name: CLI Publish NPM Package
uses: ./.github/actions/publish-npm-cli
with:
ref: 'master'
github-token: ${{ secrets.GITHUB_TOKEN }}
npm-token: ${{ secrets.NPM_TOKEN }}
npm-package-name: 'dotcli'
npm-package-scope: '@dotcms'
node-version: '19'
workflow-to-search: 'build-test-main.yml'
artifact-id: 'cli-artifacts-*'
```
275 changes: 275 additions & 0 deletions .github/actions/publish-npm-cli/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
name: 'CLI Publish NPM Package'
description: 'Package the dotCMS CLI as an NPM project.'
inputs:
ref:
description: 'Branch to build from'
required: false
default: 'master'
github-token:
description: 'GitHub Token'
required: true
npm-token:
description: 'NPM Token'
required: true
npm-package-name:
description: 'NPM package name'
required: false
default: 'dotcli'
npm-package-scope:
description: 'NPM package scope'
required: false
default: '@dotcms'
node-version:
description: 'Node.js version'
required: false
default: '19'
artifact-id:
description: 'Artifact id'
required: false
default: 'cli-artifacts-*'
reuse-previous-build:
description: 'Indicates if the workflow should reuse the previous build'
required: false

runs:
using: "composite"
steps:
- name: 'Checkout'
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref }}

- name: 'Set up Node.js'
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}

- name: 'Install Jinja2'
run: pip install jinja2-cli
shell: bash

- name: 'Download Build Artifact'
id: data-download
uses: dawidd6/[email protected]
with:
github_token: ${{ inputs.github-token }}
#workflow: ${{ inputs.workflow-to-search }}
commit: ${{ github.sha }}
workflow_conclusion: success
search_artifacts: true
dry_run: true
name: ${{ inputs.artifact-id }}
name_is_regexp: true
path: .
if_no_artifact_found: warn

- name: 'Check if artifact exists'
id: check
run: |
build_artifact_exists=${{ steps.data-download.outputs.found_artifact }}
if [[ ${build_artifact_exists} == "true" ]]; then
run_id=`echo '${{ steps.data-download.outputs.artifacts }}' | jq -r '.[0].workflow_run.id'`
found_artifacts=true
echo "Artifact Run id: $run_id"
else
echo "No artifact found"
run_id="${{ github.run_id }}"
found_artifacts=false
fi
echo "run_id=$run_id" >> $GITHUB_OUTPUT
echo "found_artifacts=$found_artifacts" >> $GITHUB_OUTPUT
shell: bash

- name: Download Previous Build Artifact
id: data-download-previous
uses: dawidd6/[email protected]
if: ${{ inputs.reuse-previous-build == true && steps.data-download.outputs.found_artifact == 'false' }}
with:
github_token: ${{ inputs.github-token }}
#workflow: build-test-merge_group.yml
workflow_search: true
#commit: ${{ github.sha }}
workflow_conclusion: success
search_artifacts: true
dry_run: true
name: ${{ inputs.artifact-id }}
path: .
if_no_artifact_found: warn

- name: 'Check if any previous artifact exists'
id: check-previous
if: ${{ inputs.reuse-previous-build == true && steps.data-download.outputs.found_artifact == 'false' }}
run: |
build_artifact_exists=${{ steps.data-download-previous.outputs.found_artifact }}
if [[ ${build_artifact_exists} == "true" ]]; then
run_id=`echo '${{ steps.data-download-previous.outputs.artifacts }}' | jq -r '.[0].workflow_run.id'`
found_artifacts=true
echo "Artifact Run id: $run_id"
else
echo "No artifact found"
run_id="${{ github.run_id }}"
found_artifacts=false
fi
echo "run_id=$run_id" >> $GITHUB_OUTPUT
echo "found_artifacts=$found_artifacts" >> $GITHUB_OUTPUT
shell: bash

- name: 'Download all build artifacts'
id: download-cli-artifacts
uses: actions/download-artifact@v4
with:
pattern: ${{ inputs.artifact-id }}
path: ${{ github.workspace }}/artifacts
github-token: ${{ inputs.github-token }} # token with actions:read permissions on target repo
merge-multiple: true
run-id: ${{ steps.check.outputs.run_id }}

- name: 'List CLI Artifacts'
run: |
echo "::group::CLI Artifacts"
echo "Artifacts"
ls -R ${{ github.workspace }}/artifacts
echo "::endgroup::"
shell: bash

- name: 'Extract run-id'
id: extract-metadata
run: |
echo "run-id=${{ github.run_id }}" >> $GITHUB_OUTPUT
shell: bash

- name: 'Extract package version'
id: project
run: |
echo "::group::Extract package version"
version=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout -pl :dotcms-cli)
echo "::debug::PROJECT VERSION: $version"
echo "version=$version" >> $GITHUB_OUTPUT
echo "::endgroup::"
shell: bash
# Determines the NPM package version and tag
# Distinguishes between snapshots and releases

- name: 'Dynamic configuration of NPM package Version and Tag'
id: npm-version-tag
run: |
echo "::group::NPM package Version and Tag"
MVN_PACKAGE_VERSION=$(echo ${{ steps.project.outputs.version }} | tr '[:lower:]' '[:upper:]')
PACKAGE_FULL_NAME=${{ inputs.npm-package-scope }}/${{ inputs.npm-package-name }}
# Check if the npm package exists
if ! npm view $PACKAGE_FULL_NAME &> /dev/null; then
echo "::error::The package $PACKAGE_FULL_NAME does not exist on npm."
exit 1
fi
# Check if the package is a snapshot
REGEX="([0-9]+\.[0-9]+\.[0-9]+)-SNAPSHOT"
if [[ $MVN_PACKAGE_VERSION =~ $REGEX ]]; then
echo "::debug::Snapshot version found."
NPM_PACKAGE_VERSION_TAG="rc"
MVN_BASE_VERSION="${BASH_REMATCH[1]}"
# Use regular expression to extract version components
if [[ $MVN_BASE_VERSION =~ ([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then
MAJOR=$(echo "${BASH_REMATCH[1]}" | sed "s/\b0\+\([1-9]\)/\1/g")
MINOR=$(echo "${BASH_REMATCH[2]}" | sed "s/\b0\+\([1-9]\)/\1/g")
PATCH=$(echo "${BASH_REMATCH[3]}" | sed "s/\b0\+\([1-9]\)/\1/g")
VERSION_NPM_FORMAT="${MAJOR}.${MINOR}.${PATCH}"
echo "::debug::VERSION_NPM_FORMAT: ${VERSION_NPM_FORMAT}"
else
echo "::error::Invalid Maven version format: $MVN_BASE_VERSION"
exit 1
fi
echo "VERSION_NPM_FORMAT: ${VERSION_NPM_FORMAT}"
LAST_RC_VERSIONS=$(npm view $PACKAGE_FULL_NAME versions --json)
echo "LAST_RC_VERSIONS: ${LAST_RC_VERSIONS}"
LAST_RC_VERSION=$(npm view $PACKAGE_FULL_NAME versions --json | jq --arg filter $VERSION_NPM_FORMAT 'map(.| select(. | contains($filter)))' | jq -r 'map(select(test("-rc\\d+$"))) | max')
if [[ $LAST_RC_VERSION == "$VERSION_NPM_FORMAT"* ]]; then
NEXT_RC_VERSION=$(echo "$LAST_RC_VERSION" | awk -F '-rc' '{print $1 "-rc" $2 + 1}')
RC_SUFFIX=$(echo "$NEXT_RC_VERSION" | sed -n 's/.*-rc\([0-9]*\)/-rc\1/p')
else
RC_SUFFIX="-rc1"
fi;
NPM_PACKAGE_VERSION=${MVN_BASE_VERSION}${RC_SUFFIX}
else
echo "::debug::Release version found."
NPM_PACKAGE_VERSION_TAG="latest"
NPM_PACKAGE_VERSION=${MVN_PACKAGE_VERSION}
fi;
echo "::debug::NPM_PACKAGE_VERSION: $NPM_PACKAGE_VERSION"
echo "::debug::NPM_PACKAGE_VERSION_TAG: $NPM_PACKAGE_VERSION_TAG"
echo "npm-package-version=$NPM_PACKAGE_VERSION" >> $GITHUB_OUTPUT
echo "npm-package-version-tag=$NPM_PACKAGE_VERSION_TAG" >> $GITHUB_OUTPUT
echo "::endgroup::"
shell: bash


# Sets up the NPM package
# Creates the bin folder with the binaries
# Adds the postinstall.js script
# Generates the package.json file with Jinja2

- name: 'NPM Package setup'
working-directory: ${{ github.workspace }}/tools/dotcms-cli/npm/
env:
NPM_PACKAGE_NAME: ${{ inputs.npm-package-name }}
NPM_PACKAGE_VERSION: ${{ steps.npm-version-tag.outputs.npm-package-version }}
MVN_PACKAGE_NAME: dotcms-cli
MVN_PACKAGE_VERSION: ${{ steps.project.outputs.version }}
run: |
echo "::group::NPM Package setup"
echo "Adding bin folder with all the binaries"
mkdir -p bin
find ${{ github.workspace }}/artifacts/cli/target/distributions/ -name "*.zip" -exec unzip -d bin {} \;
echo "Adding wrapper script"
mv src/postinstall.js.seed src/postinstall.js
echo "Adding README.md file"
cp ${{ github.workspace }}/tools/dotcms-cli/README.md .
echo "Adding package.json file"
jinja2 package.j2 -D packageName=${MVN_PACKAGE_NAME} -D npmPackageName=${NPM_PACKAGE_NAME} -D npmPackageVersion=${NPM_PACKAGE_VERSION} -D packageVersion=${MVN_PACKAGE_VERSION} --format json -o package.json
rm -f package.j2
cat package.json
cat src/postinstall.js
echo "::endgroup::"
shell: bash

- name: 'NPM Package tree'
run: ls -R ${{ github.workspace }}/tools/dotcms-cli/npm/
shell: bash

- name: 'Validate NPM package'
working-directory: ${{ github.workspace }}/tools/dotcms-cli/npm
run: |
echo "::group::NPM package contents"
if [ ! -f package.json ]; then
echo "::error::NPM package not found. Exiting..."
exit 1
else
echo "::notice::NPM package found. Proceeding..."
cat package.json
fi
echo "::endgroup::"
shell: bash

- name: 'Publish to NPM registry'
working-directory: ${{ github.workspace }}/tools/dotcms-cli/npm
env:
NPM_AUTH_TOKEN: ${{ inputs.npm-token }}
NPM_PACKAGE_VERSION_TAG: ${{ steps.npm-version-tag.outputs.npm-package-version-tag }}
run: |
echo "//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}" > ~/.npmrc
npm publish --access public --tag ${NPM_PACKAGE_VERSION_TAG}
shell: bash
7 changes: 7 additions & 0 deletions .github/workflows/build-test-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ on:
type: boolean
description: 'Indicates if the workflow should build on missing artifacts'
default: true
publish-npm-package:
type: boolean
description: 'Indicates if the workflow should publish the NPM package on the registry'
default: false
run-all-tests:
description: 'Run all tests'
type: boolean
Expand Down Expand Up @@ -60,12 +64,15 @@ jobs:
artifact-run-id: ${{ needs.initialize.outputs.artifact-run-id }}
environment: nightly
deploy-dev-image: true
publish-npm-package: ${{ ( github.event_name == 'workflow_dispatch' && inputs.publish-npm-package == true ) || github.event_name == 'schedule' }}
reuse-previous-build: ${{ inputs.reuse-previous-build || false }}
secrets:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
EE_REPO_USERNAME: ${{ secrets.EE_REPO_USERNAME }}
EE_REPO_PASSWORD: ${{ secrets.EE_REPO_PASSWORD }}
DEVELOPERS_SLACK_WEBHOOK: ${{ secrets.DEVELOPERS_SLACK_WEBHOOK }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
finalize:
name: Finalize
if: always()
Expand Down
Loading

0 comments on commit 4643ce6

Please sign in to comment.