diff --git a/.github/workflows/test-action-yarn-cache.yml b/.github/workflows/test-action-yarn-cache.yml index 7cec3d0..143630d 100644 --- a/.github/workflows/test-action-yarn-cache.yml +++ b/.github/workflows/test-action-yarn-cache.yml @@ -1,8 +1,16 @@ -name: Test Yarn Cache Action +name: Test Yarn Cache on: pull_request: - paths: [actions/yarn-cache/**] + paths: + - .github/workflows/yarn-cache.yml + - actions/yarn-cache/** + - actions/yarn-cache-key/** + - yarn-cache.yml + +concurrency: + group: test-action-yarn-cache-${{ github.head_ref }} + cancel-in-progress: true env: CACHE_PATH: | @@ -12,29 +20,65 @@ env: WORKING_DIR: ./test/actions/yarn-cache jobs: - first-cache: + create-cache: + uses: ./.github/workflows/yarn-cache.yml + with: + cache-path: | + ~/.cache + test/actions/yarn-cache/.yarn/unplugged + test/actions/yarn-cache/.yarn/install-state.gz + working-directory: ./test/actions/yarn-cache + + use-exact-cache: + needs: [create-cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: - node-version-file: test/actions/yarn-cache/.nvmrc + node-version-file: ${{ env.WORKING_DIR }}/.nvmrc - run: corepack enable - - uses: ./actions/yarn-cache + - uses: ./.github/actions/yarn-cache + id: cache with: - cache_path: ${{ env.CACHE_PATH }} + cache-path: ${{ env.CACHE_PATH }} working-directory: ${{ env.WORKING_DIR }} + - if: steps.cache.outputs.cache-hit != 'true' + run: | + echo "::error::The cache wasn't used" + exit 1 - second-cache: - needs: [first-cache] + use-partial-cache: + needs: [create-cache] runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: - node-version-file: test/actions/yarn-cache/.nvmrc + node-version-file: ${{ env.WORKING_DIR }}/.nvmrc - run: corepack enable - - uses: ./actions/yarn-cache + - working-directory: ${{ env.WORKING_DIR }} + run: yarn add express@^5.2.1 + - uses: ./.github/actions/yarn-cache-key + id: key + with: + working-directory: ${{ env.WORKING_DIR }} + - uses: ./.github/actions/yarn-cache + id: cache with: - cache_path: ${{ env.CACHE_PATH }} + cache-path: ${{ env.CACHE_PATH }} working-directory: ${{ env.WORKING_DIR }} + - if: steps.cache.outputs.cache-hit == 'true' + run: | # shell + echo "::error::Expected a restore-key match, not an exact cache hit" + exit 1 + + clean-up: + needs: [create-cache, use-exact-cache, use-partial-cache] + if: always() + runs-on: ubuntu-latest + steps: + - env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_REPO: ${{ github.repository }} + run: gh cache delete --all diff --git a/.github/workflows/yarn-cache.yml b/.github/workflows/yarn-cache.yml index cec6f51..7bc3ad7 100644 --- a/.github/workflows/yarn-cache.yml +++ b/.github/workflows/yarn-cache.yml @@ -1,33 +1,70 @@ name: Yarn Cache +# description: | #markdown +# Similar idea to the yarn-cache action, but makes sure that only one workflow +# can create the cache at a time. EG: + +# ```yaml +# jobs: +# cache: +# uses: johngeorgewright/workflows/workflows/yarn-cache@main +# test: +# needs: [cache] +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v6 +# +# # OPTION 1: manually download the cache +# - uses: actions/cache/restore@v5 +# with: +# key: ${\{ needs.cache.outputs.key }} +# path: ${\{ needs.cache.outputs.path }} +# restore-keys: ${\{ needs.cache.outputs.restore-keys }} +# +# # OPTION 2: let the key be discovered automatically +# - uses: actions/setup-node@v6 +# - uses: johngeorgewright/workflows/actions/yarn-cache@main +# ``` + on: workflow_call: inputs: + cache-path: + description: The cache paths + required: false + type: string + default: | + ~/.cache + .yarn/unplugged + .yarn/install-state.gz + runs-on: default: ubuntu-latest required: false type: string description: Use runs-on to define the type of machine to run the job on. + working-directory: + description: Optional directory to execute yarn command in + required: false + type: string + default: ${{ github.workspace }} + outputs: key: value: ${{ jobs.key.outputs.cache-key }} path: value: ${{ jobs.prime.outputs.cache-path }} + restore-keys: + value: ${{ jobs.key.outputs.cache-restore-keys }} jobs: - deprecate: - name: Deprecated - runs-on: ${{ inputs.runs-on }} - steps: - - name: Message - run: echo "::warning title=Deprecated workflow::The johngeorgewright/workflows/.github/workflows/yarn-cache.yml is dperecated. Please use johngeorgewright/workflows/yarn-cache.yml instead." - key: name: Key runs-on: ${{ inputs.runs-on }} outputs: - cache-key: ${{ steps.key.outputs.cache_key }} + cache-key: ${{ steps.key.outputs.cache-key }} + cache-restore-keys: ${{ steps.key.outputs.cache-restore-keys }} steps: - name: Checkout project uses: actions/checkout@v6 @@ -36,46 +73,36 @@ jobs: id: node uses: actions/setup-node@v6 with: - node-version-file: .nvmrc + node-version-file: ${{ inputs.working-directory }}/.nvmrc + + - run: corepack enable - name: Key id: key - run: | - corepack enable - echo "cache_key=${{ runner.os }}-node-${{ steps.node.outputs.node-version }}-yarn-$(yarn --version)-${{ hashFiles('**/yarn.lock') }}" >> $GITHUB_OUTPUT + uses: johngeorgewright/workflows/actions/yarn-cache-key@main + with: + working-directory: ${{ inputs.working-directory }} prime: name: Prime needs: [key] runs-on: ${{ inputs.runs-on }} - env: - CACHE_PATH: | - ~/.cache - .yarn/unplugged - .yarn/install-state.gz concurrency: group: ${{ needs.key.outputs.cache-key }} outputs: - cache-path: ${{ env.CACHE_PATH }} + cache-path: ${{ inputs.cache-path }} steps: - name: Checkout project uses: actions/checkout@v6 - name: Install Node.js - id: node uses: actions/setup-node@v6 with: - node-version-file: .nvmrc + node-version-file: ${{ inputs.working-directory }}/.nvmrc - - name: Cache - id: cache - uses: actions/cache@v5 - with: - path: ${{ env.CACHE_PATH }} - key: ${{ needs.key.outputs.cache-key }} + - run: corepack enable - - name: Install Depedencies - if: steps.cache.outputs.cache-hit != 'true' - run: | - corepack enable - yarn --immutable + - uses: johngeorgewright/workflows/actions/yarn-cache@main + with: + cache-path: ${{ inputs.cache-path }} + working-directory: ${{ inputs.working-directory }} diff --git a/actions/yarn-cache-key/action.yml b/actions/yarn-cache-key/action.yml index 21ed38c..1b60bb5 100644 --- a/actions/yarn-cache-key/action.yml +++ b/actions/yarn-cache-key/action.yml @@ -1,25 +1,43 @@ name: Yarn Cache Key -author: johngeorgewright +author: DMG Ads -description: Create the Yarn dependency cache key. +description: Create a cache key for you Yarn dependencies inputs: working-directory: - description: An optional working directory to discover the cache key + description: Optional directory to execute in required: false outputs: - cache-key: + cache-key: description: The cache key value: ${{ steps.key.outputs.cache-key }} + cache-restore-keys: + description: Fallback/restore keys + value: ${{ steps.key.outputs.cache-restore-keys }} runs: using: composite steps: - - shell: bash - id: key + - id: key working-directory: ${{ inputs.working-directory }} - run: | - corepack enable - echo "cache-key=${{ runner.os }}-node-$(node --version)-yarn-$(yarn --version)-${{ hashFiles('**/yarn.lock') }}" >> $GITHUB_OUTPUT + shell: bash + run: | # shell + os='${{ runner.os }}' + node_version=$( node --version ) + yarn_version=$( yarn --version ) + deps_hash=$(sed -n 's/ resolution: "\(.*\)@npm:.*/\1/p' yarn.lock \ + | sort -u \ + | sha256sum \ + | cut -d ' ' -f 1) + + echo "cache-key=deps-${os}-${node_version}-${yarn_version}-${deps_hash}-${{ hashFiles('**/yarn.lock') }}" >> $GITHUB_OUTPUT + + echo "cache-restore-keys<> $GITHUB_OUTPUT + echo "deps-${os}-${node_version}-${yarn_version}-${deps_hash}-" >> $GITHUB_OUTPUT + echo "deps-${os}-${node_version}-${yarn_version}-" >> $GITHUB_OUTPUT + echo "deps-${os}-${node_version}-" >> $GITHUB_OUTPUT + echo "deps-${os}-" >> $GITHUB_OUTPUT + echo "deps-" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT diff --git a/actions/yarn-cache/action.yml b/actions/yarn-cache/action.yml index 5e520ca..3dd2fa9 100644 --- a/actions/yarn-cache/action.yml +++ b/actions/yarn-cache/action.yml @@ -1,12 +1,13 @@ name: Yarn Cache -author: johngeorgewright +author: DMG Ads description: Speed up your Node.js builds by caching the Yarn dependencies. inputs: - cache_path: + cache-path: description: The cache paths + required: false default: | ~/.cache .yarn/unplugged @@ -15,19 +16,48 @@ inputs: description: Optional directory to execute in required: false +outputs: + cache-key: + description: The cache key + value: ${{ steps.key.outputs.cache-key }} + cache-hit: + description: Whether the cache was used + value: ${{ steps.cache.outputs.cache-hit }} + cache-restore-keys: + description: Fallback/restore keys + value: ${{ steps.key.outputs.cache-restore-keys }} + runs: using: composite steps: - id: key - uses: johngeorgewright/workflows/actions/yarn-cache-key@main + uses: johngeorgewright/workflows/actions/yarn-cache-key@yarn-cache + with: + working-directory: ${{ inputs.working-directory }} - - id: cache - uses: actions/cache@v4 + - name: Restore Cache + id: cache + uses: actions/cache/restore@v5 with: - path: ${{ inputs.cache_path }} key: ${{ steps.key.outputs.cache-key }} + path: ${{ inputs.cache-path }} + restore-keys: ${{ steps.key.outputs.cache-restore-keys }} - - if: steps.cache.outputs.cache-hit != 'true' + - name: Clean Stale Install State + if: steps.cache.outputs.cache-matched-key != '' && steps.cache.outputs.cache-matched-key != steps.key.outputs.cache-key + working-directory: ${{ inputs.working-directory }} + shell: bash + run: rm -f .yarn/install-state.gz + + - name: Install Dependencies + if: steps.cache.outputs.cache-hit != 'true' working-directory: ${{ inputs.working-directory }} shell: bash run: yarn --immutable + + - name: Save Cache + if: steps.cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v5 + with: + path: ${{ inputs.cache-path }} + key: ${{ steps.key.outputs.cache-key }} diff --git a/yarn-cache.yml b/yarn-cache.yml index fa728d6..3e7f85d 100644 --- a/yarn-cache.yml +++ b/yarn-cache.yml @@ -1,19 +1,62 @@ name: Yarn Cache +# description: | #markdown +# Similar idea to the yarn-cache action, but makes sure that only one workflow +# can create the cache at a time. EG: + +# ```yaml +# jobs: +# cache: +# uses: johngeorgewright/workflows/workflows/yarn-cache@main +# test: +# needs: [cache] +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v6 +# +# # OPTION 1: manually download the cache +# - uses: actions/cache/restore@v5 +# with: +# key: ${\{ needs.cache.outputs.key }} +# path: ${\{ needs.cache.outputs.path }} +# restore-keys: ${\{ needs.cache.outputs.restore-keys }} +# +# # OPTION 2: let the key be discovered automatically +# - uses: actions/setup-node@v6 +# - uses: johngeorgewright/workflows/actions/yarn-cache@main +# ``` + on: workflow_call: inputs: - runs-on: + cache-path: + description: The cache paths + required: false + type: string + default: | + ~/.cache + .yarn/unplugged + .yarn/install-state.gz + + runs-on: default: ubuntu-latest required: false type: string description: Use runs-on to define the type of machine to run the job on. + working-directory: + description: Optional directory to execute yarn command in + required: false + type: string + default: ${{ github.workspace }} + outputs: key: value: ${{ jobs.key.outputs.cache-key }} path: value: ${{ jobs.prime.outputs.cache-path }} + restore-keys: + value: ${{ jobs.key.outputs.cache-restore-keys }} jobs: key: @@ -21,6 +64,7 @@ jobs: runs-on: ${{ inputs.runs-on }} outputs: cache-key: ${{ steps.key.outputs.cache-key }} + cache-restore-keys: ${{ steps.key.outputs.cache-restore-keys }} steps: - name: Checkout project uses: actions/checkout@v6 @@ -29,44 +73,36 @@ jobs: id: node uses: actions/setup-node@v6 with: - node-version-file: .nvmrc + node-version-file: ${{ inputs.working-directory }}/.nvmrc + + - run: corepack enable - name: Key id: key - uses: johngeorgewright/workflows/actions/yarn-cache-key.yml@main + uses: johngeorgewright/workflows/actions/yarn-cache-key@yarn-cache + with: + working-directory: ${{ inputs.working-directory }} prime: name: Prime needs: [key] runs-on: ${{ inputs.runs-on }} - env: - CACHE_PATH: | - ~/.cache - .yarn/unplugged - .yarn/install-state.gz concurrency: group: ${{ needs.key.outputs.cache-key }} outputs: - cache-path: ${{ env.CACHE_PATH }} + cache-path: ${{ inputs.cache-path }} steps: - name: Checkout project uses: actions/checkout@v6 - name: Install Node.js - id: node uses: actions/setup-node@v6 with: - node-version-file: .nvmrc + node-version-file: ${{ inputs.working-directory }}/.nvmrc - - name: Cache - id: cache - uses: actions/cache@v4 - with: - path: ${{ env.CACHE_PATH }} - key: ${{ needs.key.outputs.cache-key }} + - run: corepack enable - - name: Install Depedencies - if: steps.cache.outputs.cache-hit != 'true' - run: | - corepack enable - yarn --immutable + - uses: johngeorgewright/workflows/actions/yarn-cache@yarn-cache + with: + cache-path: ${{ inputs.cache-path }} + working-directory: ${{ inputs.working-directory }}