diff --git a/flake.nix b/flake.nix index aa172abd..77d9f5d4 100644 --- a/flake.nix +++ b/flake.nix @@ -31,6 +31,7 @@ pkgs.writeShellApplication { name = "write"; text = '' + mkdir -p "$(dirname ${path})" cat > ${path} <<'EOF' ${value} EOF @@ -78,6 +79,23 @@ description = "write action.yml-s and tables for README-s"; }; + writeBuildjetCI = writeYAML ".github/workflows/buildjet-ci.yaml" ( + import ./nix/ci.nix { backend = "buildjet"; } + ); + + writeActionsCI = writeYAML ".github/workflows/ci.yaml" ( + import ./nix/ci.nix { backend = "actions"; } + ); + + writeCI = { + text = '' + ${lib.getExe config.packages.writeBuildjetCI} + ${lib.getExe config.packages.writeActionsCI} + npm run format-ci + ''; + description = "write CI files"; + }; + install = { runtimeInputs = [ pkgs.nodejs_20 ]; text = ''npm i''; @@ -97,7 +115,7 @@ { prefix = "nix run .#"; packages = { - inherit (config.packages) write install build; + inherit (config.packages) write install build writeCI; }; } ]; diff --git a/nix/ci.nix b/nix/ci.nix new file mode 100644 index 00000000..3221a95a --- /dev/null +++ b/nix/ci.nix @@ -0,0 +1,330 @@ +{ backend }: +let + choose = + optionActions: optionBuildjet: if backend == "actions" then optionActions else optionBuildjet; + + backend_ = choose "" '' + # use BuildJet backend + backend: buildjet''; + + on = choose '' + push: + # don't run on tags, run on commits + # https://github.com/orgs/community/discussions/25615 + tags-ignore: + - "**" + branches: + - "**" + schedule: + - cron: 0 0 * * *'' ""; + + name = choose "" ''with BuildJet backend''; +in +'' + name: Nix CI ${name} + on: + ${on} + pull_request: + workflow_dispatch: + + env: + # https://stackoverflow.com/a/71158878 + git_pull: git pull --rebase origin ''${{ github.head_ref || github.ref_name }} + pin_nixpkgs: nix registry pin nixpkgs github:NixOS/nixpkgs/807c549feabce7eddbf259dbdcec9e0600a0660d + + jobs: + # Build the action + # Commit and push the built code + build: + name: Build the action + runs-on: ubuntu-20.04 + permissions: + contents: write + actions: write + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - uses: nixbuild/nix-quick-install-action@v27 + + - name: Restore and save Nix store + uses: ./. + with: + primary-key: build-''${{ runner.os }}-''${{ hashFiles('**/package-lock.json', 'package.json', 'flake.nix', 'flake.lock') }} + paths: | + ~/.npm + # do purge caches + purge: true + # purge all versions of the cache + purge-prefixes: build-''${{ runner.os }}- + # created more than 0 seconds ago relative to the start of the `Post Restore` phase + purge-created: 0 + # except the version with the `primary-key`, if it exists + purge-primary-key: never + ${backend_} + + # # Uncomment to debug this job + # - name: Setup tmate session + # uses: mxschmitt/action-tmate@v3 + + - name: Configure git + env: + # required for gh + GITHUB_TOKEN: ''${{ secrets.GITHUB_TOKEN }} + run: | + ''${{ github.head_ref && format('gh pr checkout {0}', github.event.pull_request.number) || ${"''"}}} + + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Rebase + run: ''${{ env.git_pull }} + + - name: Install packages & Build the action + run: nix run .#install + + - name: Update docs + run: nix run .#write + + - name: Commit & push changes + run: | + git add dist + git commit -m "chore: build the action" || echo "Nothing to commit" + git add . + git commit -m "chore: update docs" || echo "Nothing to commit" + git push + + # Make `individual` caches + # Restore `individual` or `common` caches + # Usually, there should be no `individual` caches to restore as they're purged by `merge-similar-caches` + make-similar-caches: + name: Make similar caches + needs: build + permissions: + actions: write + strategy: + matrix: + os: + - macos-11 + - macos-12 + - ubuntu-20.04 + - ubuntu-22.04 + id: + - 1 + - 2 + runs-on: ''${{ matrix.os }} + steps: + - name: Checkout this repo + uses: actions/checkout@v4 + + - name: Rebase + run: ''${{ env.git_pull }} + + - uses: nixbuild/nix-quick-install-action@v27 + + - name: Restore and save Nix store - ''${{ matrix.id }} + uses: ./. + with: + # save a new cache every time `ci.yaml` changes + primary-key: similar-cache-''${{ matrix.os }}-individual-''${{ matrix.id }}-''${{ hashFiles('.github/workflows/ci.yaml') }} + # otherwise, restore a common cache if and only if it matches the current `ci.yaml` + restore-prefixes-first-match: similar-cache-''${{ matrix.os }}-common-''${{ hashFiles('.github/workflows/ci.yaml') }} + # do purge caches + purge: true + # purge all versions of the cache + purge-prefixes: similar-cache-''${{ matrix.os }}-individual-''${{ matrix.id }}- + # created more than 0 seconds ago relative to the start of the `Post Restore` phase + purge-created: 0 + # except the version with the `primary-key`, if it exists + purge-primary-key: never + ${backend_} + + - name: Pin nixpkgs + run: ''${{ env.pin_nixpkgs }} + + - name: Install nixpkgs#poetry + if: ''${{ matrix.id == 1 }} + run: nix profile install nixpkgs#poetry + + - name: Install nixpkgs#nodejs + if: ''${{ matrix.id == 2 }} + run: nix profile install nixpkgs#nodejs + + # Merge similar `individual` caches + # Purge `individual` caches and old `common` caches + # Save new `common` caches + merge-similar-caches: + name: Merge similar caches + needs: make-similar-caches + permissions: + actions: write + strategy: + matrix: + os: + - macos-11 + - macos-12 + - ubuntu-20.04 + - ubuntu-22.04 + runs-on: ''${{ matrix.os }} + steps: + - name: Checkout this repo + uses: actions/checkout@v4 + + - name: Rebase + run: ''${{ env.git_pull }} + + - uses: nixbuild/nix-quick-install-action@v27 + + - name: Restore and save Nix store + uses: ./. + with: + primary-key: similar-cache-''${{ matrix.os }}-common-''${{ hashFiles('.github/workflows/ci.yaml') }} + # when there's a common cache hit, don't restore individual caches + skip-restore-on-hit-primary-key: true + # otherwise, restore individual caches, but not their old versions + restore-prefixes-all-matches: | + similar-cache-''${{ matrix.os }}-individual-1-''${{ hashFiles('.github/workflows/ci.yaml') }} + similar-cache-''${{ matrix.os }}-individual-2-''${{ hashFiles('.github/workflows/ci.yaml') }} + # do purge caches + purge: true + # purge all versions of the cache + purge-prefixes: similar-cache-''${{ matrix.os }}-common- + # created more than 0 seconds ago relative to the start of the `Post Restore` phase + purge-created: 0 + # except the version with the `primary-key`, if it exists + purge-primary-key: never + ${backend_} + + - name: Pin nixpkgs + run: ''${{ env.pin_nixpkgs }} + + # Stuff in a profile can survive garbage collection. + # Therefore, profiles are ignored when restoring a cache. + # So, the current profile should be the default profile created by nix-quick-install-action. + # The default profile should contain only nix. + - name: List profile (should be empty) + run: nix profile list + + - name: Check that the profile is empty + shell: bash + run: | + nix profile list \ + | grep 'Index' \ + | wc -l \ + | xargs test 1 -eq + + - name: Install nixpkgs#poetry + run: nix profile install nixpkgs#poetry + + - name: Install nixpkgs#nodejs + run: nix profile install nixpkgs#nodejs + + - name: Run poetry + run: poetry --version + + - name: Run node + run: node --version + + compare-run-times: + name: Job with caching + needs: merge-similar-caches + permissions: + actions: write + strategy: + matrix: + do-cache: + - true + - false + os: + - macos-11 + - macos-12 + - ubuntu-20.04 + - ubuntu-22.04 + runs-on: ''${{ matrix.os }} + steps: + - name: Checkout this repo + uses: actions/checkout@v4 + + - name: Rebase + run: ''${{ env.git_pull }} + + - uses: nixbuild/nix-quick-install-action@v27 + + - name: Restore and save Nix store + if: ''${{ matrix.do-cache }} + uses: ./. + with: + # save a new cache every time ci file changes + primary-key: cache-''${{ matrix.os }}-''${{ hashFiles('.github/workflows/ci.yaml') }} + restore-prefixes-first-match: cache-''${{ matrix.os }}- + # do purge caches + purge: true + # purge all versions of the cache + purge-prefixes: cache-''${{ matrix.os }}- + # created more than 0 seconds ago relative to the start of the `Post Restore` phase + purge-created: 0 + # except the version with the `primary-key`, if it exists + purge-primary-key: never + # and collect garbage in the Nix store until it reaches this size in bytes + gc-max-store-size: 8000000000 + ${backend_} + + # Uncomment to debug this job + # - name: Setup tmate session + # uses: mxschmitt/action-tmate@v3 + + - name: Show profile + run: nix profile list + + - name: Pin nixpkgs + run: ''${{ env.pin_nixpkgs }} + + - name: List registry + run: nix registry list + + - name: Install nixpkgs + run: nix profile install $(nix flake archive nixpkgs --json | jq -r '.path') + + - name: Show profile + run: nix profile list + + - name: Install nixpkgs#hello + run: | + nix profile install nixpkgs#hello + + - name: Install nixpkgs#cachix + run: | + nix profile install nixpkgs#cachix + + - name: Install nixpkgs#nixpkgs-fmt + run: | + nix profile install nixpkgs#nixpkgs-fmt + + - name: Install nixpkgs#alejandra + run: | + nix profile install nixpkgs#alejandra + + - name: Install nixpkgs#nixd + run: | + nix profile install nixpkgs#nixd + + - name: Install nixpkgs#ghc + run: | + nix profile install nixpkgs#ghc + + - name: Install nixpkgs#haskell-language-server + run: | + nix profile install nixpkgs#haskell-language-server + + - name: Install nixpkgs#purescript + run: | + nix profile install nixpkgs#purescript + + - name: Install nixpkgs#nodejs + run: | + nix profile install nixpkgs#nodejs + + - name: Show profile + run: nix profile list +'' diff --git a/package.json b/package.json index 8fdd6683..ce0501d1 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "build": "MERGE=\"$(cat src/templates/merge.sql)\"; printf 'export const mergeSqlTemplate = `\n%s\n`' \"$MERGE\" > src/templates/merge.ts; tsc && ncc build -o dist/restore src/restore.ts && ncc build -o dist/save src/save.ts && ncc build -o dist/restore-only src/restoreOnly.ts && ncc build -o dist/save-only src/saveOnly.ts", "test": "tsc --noEmit && jest --coverage", "lint": "eslint **/*.ts --cache", - "format": "prettier --write **/*.ts .github/workflows/ci.yaml **/*.md", + "format": "prettier --write **/*.ts **/*.md **/action.yml", + "format-ci": "prettier --write .github/workflows/*.yaml", "format-check": "prettier --check **/*.ts", "readme": "npx action-docs -u; (cd save; npx action-docs -u); (cd restore; npx action-docs -u); prettier --write restore/README.md save/README.md README.md" },