diff --git a/.github/actions/cache/action.yml b/.github/actions/cache/action.yml new file mode 100644 index 0000000..7e01f81 --- /dev/null +++ b/.github/actions/cache/action.yml @@ -0,0 +1,57 @@ +name: Getting Cache +description: 'Composite action to share cache fetching between workflows' + +inputs: + PREFIX_CACHE_KEY: + description: 'Supported string values. Used as prefix to generate a cache key' + required: false + NODE_MODULES: + description: 'Supported boolean values. Used to know if should manage the NODE MODULES cache' + required: false + NPM: + description: 'Supported boolean values. Used to know if should manage the NPM cache' + required: false + +outputs: + cache_node_modules: + description: 'Used to know if exist a NODE_MODULES cache' + value: ${{ steps.cache_node_modules.outputs.cache-hit }} + +runs: + using: composite + + steps: + - name: 'Set unify package version' + id: package + shell: sh + run: npm version -no-git-tag-version 0.0.0 + + - name: 'Get NPM cache directory path' + id: cache_path + shell: sh + run: echo dir=$(npm config get cache) >> $GITHUB_OUTPUT + + - name: 'Get cache NODE MODULES dependencies' + id: cache_node_modules + if: inputs.NODE_MODULES == 'true' + uses: actions/cache@v4 + with: + path: ./node_modules + key: ${{ inputs.PREFIX_CACHE_KEY }}-node_modules-${{ hashFiles('**/package-lock.json') }} + + - name: 'Get cache NPM dependencies' + id: cache_npm + if: inputs.NPM == 'true' && steps.cache_node_modules.outputs.cache-hit != 'true' + uses: actions/cache@v4 + with: + path: | + ~/.npm + ${{ steps.cache_path.outputs.dir }} + key: ${{ inputs.PREFIX_CACHE_KEY }}-npm-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ inputs.PREFIX_CACHE_KEY }}-npm- + ${{ inputs.PREFIX_CACHE_KEY }}- + + - name: 'Undo package version changes' + shell: sh + run: git checkout -- . diff --git a/.github/actions/increment-version/action.yml b/.github/actions/increment-version/action.yml new file mode 100644 index 0000000..5da5448 --- /dev/null +++ b/.github/actions/increment-version/action.yml @@ -0,0 +1,18 @@ +name: Increment Version + +outputs: + version: + description: "New version" + value: ${{ steps.project.outputs.version }} + previous: + description: "Old version" + value: ${{ steps.project.outputs.previous }} + +runs: + using: composite + + steps: + - name: 'Increment version' + id: project + shell: sh + run: ${GITHUB_ACTION_PATH}/increment-version.sh diff --git a/.github/actions/increment-version/increment-version.sh b/.github/actions/increment-version/increment-version.sh new file mode 100755 index 0000000..2a7919f --- /dev/null +++ b/.github/actions/increment-version/increment-version.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e + +# GET CURRENT VERSION +CURRENT_BRANCH_VERSION=$(node -p "require('./package.json').version") + +echo "Current version: v$CURRENT_BRANCH_VERSION" +echo previous=$CURRENT_BRANCH_VERSION >> $GITHUB_OUTPUT +echo version=$CURRENT_BRANCH_VERSION >> $GITHUB_OUTPUT + +# GET LAST RELEASE VERSION AND COMMITS +git fetch --tags +LAST_RELEASE_TAG=$(git tag -l "v*" | sort -V | grep -E "^v[0-9]+(\.[0-9]+)*$" | tail -1) +if [ -z "$LAST_RELEASE_TAG" ]; then + LAST_RELEASE_VERSION="0.0.0" + echo "There is no last release, starting from v$LAST_RELEASE_VERSION" + LAST_RELEASE_ID=$(git rev-list --max-parents=0 HEAD) +else + LAST_RELEASE_VERSION=$(expr "$LAST_RELEASE_TAG" : "v\(.*\)") + echo "Last released version: v$LAST_RELEASE_VERSION" + LAST_RELEASE_ID=$LAST_RELEASE_TAG +fi + +COMMITS=$(git log --pretty=format:"%B" "$LAST_RELEASE_ID"..) +if [ "$COMMITS" == "" ]; then + echo "No changes from the last version ($LAST_RELEASE_VERSION). No version number change required." + exit 0; +fi + +# FIGURE OUT NEXT VERSION +if echo "$COMMITS" | grep -qE "^BREAKING[ -]CHANGE: "; then + CURRENT_MAJOR=$(echo "$LAST_RELEASE_VERSION" | sed -E "s|^([0-9]+)\.([0-9]+)\.([0-9]+)|\1|") + NEXT_VERSION=$(echo "$LAST_RELEASE_VERSION" | sed -E "s|([0-9]+)\.([0-9]+)\.([0-9]+)|$(( CURRENT_MAJOR + 1 )).0.0|") +elif echo "$COMMITS" | grep -qE "^feat: |^feat\(.*\): "; then + CURRENT_MINOR=$(echo "$LAST_RELEASE_VERSION" | sed -E "s|^([0-9]+)\.([0-9]+)\.([0-9]+)|\2|") + NEXT_VERSION=$(echo "$LAST_RELEASE_VERSION" | sed -E "s|([0-9]+)\.([0-9]+)\.([0-9]+)|\1.$(( CURRENT_MINOR + 1 )).0|") +else + CURRENT_PATCH=$(echo "$LAST_RELEASE_VERSION" | sed -E "s|^([0-9]+)\.([0-9]+)\.([0-9]+)|\3|") + NEXT_VERSION=$(echo "$LAST_RELEASE_VERSION" | sed -E "s|([0-9]+)\.([0-9]+)\.([0-9]+)|\1.\2.$(( CURRENT_PATCH + 1 ))|") +fi + +versions=$(printf '%s\n%s' "$NEXT_VERSION" "$CURRENT_BRANCH_VERSION") +if [ "$NEXT_VERSION" == "$CURRENT_BRANCH_VERSION" ] || [ "$versions" == "$(sort -V <<< "$versions")" ]; then + echo "Version is updated, no changes required!" >&2 + exit 0 +fi + +echo "Suggested new version: $NEXT_VERSION" +echo version=$NEXT_VERSION >> $GITHUB_OUTPUT + +## UPDATE VERSION +npm version -no-git-tag-version --force $NEXT_VERSION diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..609770b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: CI + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + ci: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + defaults: + run: + shell: sh + + steps: + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + + - name: Set Node version + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4 + with: + node-version-file: .nvmrc + + - name: Setup Node cache + id: cache + uses: ./.github/actions/cache + with: + PREFIX_CACHE_KEY: ${{ runner.os }} + NODE_MODULES: 'true' + NPM: 'true' + + - name: Install dependencies + if: steps.cache.outputs.cache_node_modules != 'true' + run: npm ci + + - name: Prettier + run: npm run format + + - name: Linter + run: npm run lint + + - name: Build + run: npm run build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..dfbb01c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,108 @@ +name: Release + +on: + push: + branches: + - main + +concurrency: release + +jobs: + release: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + defaults: + run: + shell: sh + + steps: + - name: Generate a token + id: app-token + uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1 + with: + app-id: ${{ vars.GH_TOKENS_APP_ID }} + private-key: ${{ secrets.GH_TOKENS_APP_PRIVATE_KEY }} + + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + with: + ref: refs/heads/main + token: ${{ steps.app-token.outputs.token }} + fetch-depth: 0 + + - name: Set Node version + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4 + with: + node-version-file: .nvmrc + + - name: Setup Node cache + id: cache + uses: ./.github/actions/cache + with: + PREFIX_CACHE_KEY: ${{ runner.os }} + NODE_MODULES: 'true' + NPM: 'true' + + - name: Increment and set new version + id: increment + uses: ./.github/actions/increment-version + + - name: Install dependencies + if: steps.cache.outputs.cache_node_modules != 'true' + run: npm ci + + - name: Prettier + run: npm run format + + - name: Linter + run: npm run lint + + - name: Build + run: npm run build + + - name: Commit & push changes + if: steps.increment.outputs.previous != steps.increment.outputs.version + env: + GIT_AUTHOR_NAME: GitHub Version + GIT_AUTHOR_EMAIL: 'github-actions[bot]@users.noreply.github.com' + run: | + git config --global user.name "${GIT_AUTHOR_NAME}" + git config --global user.email "${GIT_AUTHOR_EMAIL}" + git add package.json package-lock.json + git diff --quiet && git diff --staged --quiet || git commit -m "chore(version): bump version: ${{ steps.increment.outputs.version }}" -m "[skip ci]" + git pull --strategy-option=theirs origin main --no-edit --no-rebase + git push origin main + + - name: Generate tag name + id: tag + if: steps.increment.outputs.previous != steps.increment.outputs.version + run: | + echo "name=v${{steps.increment.outputs.version}}" >> $GITHUB_OUTPUT + + - name: Install GH CLI + if: steps.tag.outputs.name != '' + uses: dev-hanz-ops/install-gh-cli-action@8fff9050dae2d81b38f94500d8b74ad1d1d47410 # v0.2.0 + with: + gh-cli-version: 2.49.2 + + - name: Create version tag + if: steps.tag.outputs.name != '' + run: | + HEAD_TAG=$(git tag -l | grep -v "v*" | sort -V | tail -2 | head -1) + git tag -a "${{steps.tag.outputs.name}}" -m "release version ${{steps.tag.outputs.name}}" + git push origin "${{steps.tag.outputs.name}}" + gh release create ${{st4eps.tag.outputs.name}} --generate-notes --notes-start-tag $HEAD_TAG + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Configure NPM auth + run: | + npm config set registry="https://registry.npmjs.org/" + npm config set //registry.npmjs.org/:_authToken=${NPM_TOKEN} + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN_GRANULAR_ACCESS }} + + - name: Publish package + run: npm publish