diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a1fbcb71c..575a25a3f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -24,6 +24,10 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write strategy: fail-fast: false @@ -35,11 +39,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -64,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebb9d9b3e..4f9715239 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,39 +15,29 @@ on: schedule: - cron: 0 0 * * 1-5 +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest name: Build steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '20' check-latest: false - - name: angular build cache - uses: actions/cache@v3 - with: - path: ./.angular - key: angular-cache - - name: node_modules cache - uses: actions/cache@v3 - id: node_modules_cache - with: - path: ./node_modules - key: ${{ runner.os }}-20-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-20- - name: Install deps - if: steps.node_modules_cache.outputs.cache-hit != 'true' - run: | - npm ci + run: npm ci - name: Build run: ./tools/build.sh - name: 'Upload Artifact' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: angularfire-${{ github.run_id }} path: dist @@ -59,32 +49,26 @@ jobs: strategy: matrix: os: [ ubuntu-latest, macos-latest, windows-latest ] - node: ["18", "20", "22"] + node: ["20", "22", "24"] fail-fast: false name: Test Node ${{ matrix.node }} (${{ matrix.os }}) steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ matrix.node }} check-latest: true - - name: node_modules cache - id: node_modules_cache - uses: actions/cache@v3 - with: - path: ./node_modules - key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node }}- - name: Install deps - if: steps.node_modules_cache.outputs.cache-hit != 'true' run: npm ci - name: Download Artifacts - uses: actions/download-artifact@v4 - - name: Relocate Artifacts - run: mv angularfire-${{ github.run_id }} dist + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: angularfire-${{ github.run_id }} + path: dist - name: Test Node run: | npm run build:jasmine @@ -105,37 +89,26 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 20 check-latest: false - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 with: distribution: 'temurin' - java-version: '11' - - name: node_modules cache - id: node_modules_cache - uses: actions/cache@v3 - with: - path: ./node_modules - key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node }}- + java-version: '21' - name: Install deps - if: steps.node_modules_cache.outputs.cache-hit != 'true' run: npm ci - - name: Firebase emulator cache - uses: actions/cache@v3 - with: - path: ~/.cache/firebase/emulators - key: firebase_emulators - name: Download Artifacts - uses: actions/download-artifact@v4 - - name: Relocate Artifacts - run: mv angularfire-${{ github.run_id }} dist + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: angularfire-${{ github.run_id }} + path: dist - name: Test browser run: npm run test:${{ matrix.browser }} @@ -149,22 +122,15 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ matrix.node }} check-latest: true - - name: node_modules cache - uses: actions/cache@v3 - id: node_modules_cache - with: - path: ./node_modules - key: ${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node }}- - name: Install deps - if: steps.node_modules_cache.outputs.cache-hit != 'true' run: npm ci #- name: Lint # run: npm run lint @@ -174,16 +140,11 @@ jobs: run: | npm run build:jasmine npm run test:node - - name: Firebase emulator cache - uses: actions/cache@v3 - with: - path: ~/.cache/firebase/emulators - key: firebase_emulators - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 with: distribution: 'temurin' - java-version: '11' + java-version: '21' - name: Test headless run: npm run test:chrome-headless # Tests are flaky on Windows @@ -202,19 +163,56 @@ jobs: name: Publish (NPM) needs: ['build', 'test', 'browser'] if: ${{ github.ref == 'refs/heads/main' || github.event_name == 'release' }} + # One npm publish at a time, each held until npm lists it (about 20 minutes at most), so every + # check below reads what the previous publish wrote. + timeout-minutes: 30 + concurrency: + group: npm-publish + cancel-in-progress: false + queue: max steps: + - name: Checkout the publish scripts + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + sparse-checkout: | + tools/publish-job.js + tools/release-tag.js + sparse-checkout-cone-mode: false - name: Setup node - uses: actions/setup-node@v3 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '20' - registry-url: 'https://registry.npmjs.org' + registry-url: 'https://wombat-dressing-room.appspot.com/' check-latest: false - name: 'Download Artifacts' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: angularfire-${{ github.run_id }} + path: dist + - name: Skip a canary that is not newer than npm's + id: canary_check + if: github.event_name != 'release' + run: node tools/publish-job.js canary-check + - name: Choose the release's dist-tag + id: release_tag + if: github.event_name == 'release' + run: node tools/publish-job.js release-tag - name: Publish - run: | - cd ./angularfire-${{ github.run_id }}/packages-dist - chmod +x publish.sh - ./publish.sh + id: publish + if: steps.canary_check.outputs.skip != 'true' && steps.release_tag.outputs.published != 'true' + run: npm publish ./dist/packages-dist --access public --registry https://wombat-dressing-room.appspot.com --tag "$NPM_TAG" + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TAG: ${{ steps.release_tag.outputs.tag || 'canary' }} + - name: Wait for npm to list the version + id: wait + if: steps.publish.outcome == 'success' || steps.release_tag.outputs.published == 'true' + run: node tools/publish-job.js wait "$NPM_TAG" + env: + NPM_TAG: ${{ steps.release_tag.outputs.tag || 'canary' }} + - name: Move next up to the release + if: steps.wait.outputs.listed == 'true' && steps.release_tag.outputs.tag == 'latest' + run: node tools/publish-job.js move-next env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index beb5d455e..0e6c87259 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,6 @@ coverage api-*.json angularfire.tgz unpack.sh -publish.sh .firebase .angular .vscode \ No newline at end of file diff --git a/.npmignore b/.npmignore index 02a57a315..f1e38dce4 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,5 @@ *.spec.* test-config.* -publish.sh __ivy_ngcc__/ *.min.js *.min.js.map diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 29250ecdd..564120705 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -25,6 +25,7 @@ before sending PRs. We cannot accept code without this. - Issue number for this PR: #nnn (required) - Docs included?: (yes/no; required for all API/functional changes) - Test units included?: (yes/no; required) + - Breaking change?: (yes/no, required. If yes, add a `BREAKING CHANGE:` footer to your commit message that says what an upgrading app must change, and put `!` before the colon in the PR title and the commit subject, like `fix(auth)!: ...`) - In a clean directory, `yarn install`, `yarn test` run successfully? (yes/no; required) ### Description diff --git a/README.md b/README.md index 40c77f55e..f7084fbf2 100644 --- a/README.md +++ b/README.md @@ -12,41 +12,40 @@ by conforming to Angular conventions. - **Observable based** - Utilize RxJS rather than callbacks for real-time streams. - **NgRx friendly API** - Integrate with NgRx using AngularFire's action based APIs. - **Lazy-loading** - AngularFire dynamically imports much of Firebase, reducing the time to load your app. -- **Deploy schematics** - Get your Angular application deployed on Firebase Hosting with a single command. - **Google Analytics** - Zero-effort Angular Router awareness in Google Analytics. - **Router Guards** - Guard your Angular routes with built-in Firebase Authentication checks. ## Example use ```ts +import { ApplicationConfig } from '@angular/core'; import { provideFirebaseApp, initializeApp } from '@angular/fire/app'; import { getFirestore, provideFirestore } from '@angular/fire/firestore'; export const appConfig: ApplicationConfig = { providers: [ - provideFirebaseApp(() => initializeApp({ ... })), + provideFirebaseApp(() => initializeApp({ /* ...your Firebase config... */ })), provideFirestore(() => getFirestore()), - ... + // ... ], - ... -}) + // ... +} ``` ```ts import { AsyncPipe } from '@angular/common'; -import { inject } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { Firestore, collectionData, collection } from '@angular/fire/firestore'; interface Item { - name: string, - ... + name: string; }; @Component({ selector: 'app-root', template: `