{"_id":"@alexstormwood/gha-npm-publishing-demo","_rev":"5-ae43d3e0798b1d392c062a44079562e5","name":"@alexstormwood/gha-npm-publishing-demo","dist-tags":{"latest":"1.0.4"},"versions":{"1.0.0":{"name":"@alexstormwood/gha-npm-publishing-demo","version":"1.0.0","keywords":[],"author":"","license":"ISC","_id":"@alexstormwood/gha-npm-publishing-demo@1.0.0","maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"homepage":"https://github.com/AlexStormwood/gha-npm-publishing-demo#readme","bugs":{"url":"https://github.com/AlexStormwood/gha-npm-publishing-demo/issues"},"dist":{"shasum":"da7ec0edeac184c13bf877973b567d61a2e72cf1","tarball":"https://registry.npmjs.org/@alexstormwood/gha-npm-publishing-demo/-/gha-npm-publishing-demo-1.0.0.tgz","fileCount":9,"integrity":"sha512-gLQAe2NCYldKzb2FQaHOwKC+qiy58acWHpkztGT2YDAVgUzf8wIWIZ5locykxIwHd1o2Xs9tKkOcIN0qzNYKUw==","signatures":[{"sig":"MEQCIDH/kogrVY+6HAtXEnn9iIT9M2iVRJuU6ehpRDdna6qEAiBO0mqyNkjnnrrRWzPbbeJ7bn99o5WtScWMThG+g1aF1w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":122915},"main":"src/index.js","type":"commonjs","exports":{".":"./src/index.js"},"gitHead":"e6444794972e85b91b8a1b6baef3c959fc6e9968","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"alexstormwood","email":"alex@bigfootds.com"},"repository":{"url":"git+https://github.com/AlexStormwood/gha-npm-publishing-demo.git","type":"git"},"_npmVersion":"10.8.2","description":"Demo project to showcase automatic NPM package publishing using the newly-required OIDC auth flow.","directories":{},"_nodeVersion":"20.19.6","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/gha-npm-publishing-demo_1.0.0_1766383780959_0.8988664693136212","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@alexstormwood/gha-npm-publishing-demo","version":"1.0.1","keywords":[],"author":"","license":"ISC","_id":"@alexstormwood/gha-npm-publishing-demo@1.0.1","maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"homepage":"https://github.com/AlexStormwood/gha-npm-publishing-demo#readme","bugs":{"url":"https://github.com/AlexStormwood/gha-npm-publishing-demo/issues"},"dist":{"shasum":"08df884346b9bd00b6567d41167434ae1fb72c64","tarball":"https://registry.npmjs.org/@alexstormwood/gha-npm-publishing-demo/-/gha-npm-publishing-demo-1.0.1.tgz","fileCount":11,"integrity":"sha512-PTrR+92BHWUJCP7gQFcVLCdE/6I7ARqeQ4MiSd8G3z2yz1U20q9GWscLE0r/CK4cClrhrKMSq3cFYImPzScwYg==","signatures":[{"sig":"MEQCIB3/n6QT9+qGySef0eWNPUnkzz5WrjpxJyyo94LhR6BXAiBaieXGdjyYMLqqgaI6G65ZpTV7i75KssZpg8Lybpplkw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexstormwood%2fgha-npm-publishing-demo@1.0.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":278405},"main":"src/index.js","type":"commonjs","exports":{".":"./src/index.js"},"gitHead":"30c21444afcd7537f9b31ebc2e8d88284361cf57","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0e370243-f882-4718-a6db-c35c8db43c41"}},"repository":{"url":"git+https://github.com/AlexStormwood/gha-npm-publishing-demo.git","type":"git"},"_npmVersion":"11.6.2","description":"Demo project to showcase automatic NPM package publishing using the newly-required OIDC auth flow.","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/gha-npm-publishing-demo_1.0.1_1766384442738_0.030940235772145996","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@alexstormwood/gha-npm-publishing-demo","version":"1.0.2","keywords":[],"author":"","license":"ISC","_id":"@alexstormwood/gha-npm-publishing-demo@1.0.2","maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"homepage":"https://github.com/AlexStormwood/gha-npm-publishing-demo#readme","bugs":{"url":"https://github.com/AlexStormwood/gha-npm-publishing-demo/issues"},"dist":{"shasum":"f018f0e80796862e2c944f267545ae8913d669e3","tarball":"https://registry.npmjs.org/@alexstormwood/gha-npm-publishing-demo/-/gha-npm-publishing-demo-1.0.2.tgz","fileCount":11,"integrity":"sha512-wHDDGAFzJY/t1mF3DBhsG9mdvPgn99EkEm0DlzBUaAV+8dSRNvwjcjlYKwPr5Pt8vFrurP3AZD8z/NMHewygbA==","signatures":[{"sig":"MEYCIQD2+MF9Bisz/pBCr4VvK7WKQlEJGkJSEzKgMMgBdNM15wIhAJVOZN+fgQt2qxKzvMLJyO8TemKyuq6ylnG3R8HgJkU3","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexstormwood%2fgha-npm-publishing-demo@1.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":279372},"main":"src/index.js","type":"commonjs","exports":{".":"./src/index.js"},"gitHead":"fe4c9e72115af51dab1a8e9498ece56305d34410","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0e370243-f882-4718-a6db-c35c8db43c41"}},"repository":{"url":"git+https://github.com/AlexStormwood/gha-npm-publishing-demo.git","type":"git"},"_npmVersion":"11.6.2","description":"Demo project to showcase automatic NPM package publishing using the newly-required OIDC auth flow.","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/gha-npm-publishing-demo_1.0.2_1766384760971_0.31040730363240376","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@alexstormwood/gha-npm-publishing-demo","version":"1.0.3","keywords":[],"author":"","license":"ISC","_id":"@alexstormwood/gha-npm-publishing-demo@1.0.3","maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"homepage":"https://github.com/AlexStormwood/gha-npm-publishing-demo#readme","bugs":{"url":"https://github.com/AlexStormwood/gha-npm-publishing-demo/issues"},"dist":{"shasum":"f500151d939f8098ac18debb0efdd5127a20e42a","tarball":"https://registry.npmjs.org/@alexstormwood/gha-npm-publishing-demo/-/gha-npm-publishing-demo-1.0.3.tgz","fileCount":11,"integrity":"sha512-bl3uMmg9GHaJ0Tl8Lo3bkdh3NEQZygRyB9AmHd0JdePk4rGwZ158j2f3psA0r7In0DOpz89Da+iwNTc+ApvK9g==","signatures":[{"sig":"MEYCIQC94rGmikMxZR0ulYEwHZaIhhrggVKUlvmU5SWQjeHI2gIhAL1ekAJKiaPaN8uxiQnx8fhU5u4GhelXkLfxdQhm6WmC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexstormwood%2fgha-npm-publishing-demo@1.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":279798},"main":"src/index.js","type":"commonjs","exports":{".":"./src/index.js"},"gitHead":"6a4ba02b939afb081923658aa573dd050ab3b08b","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0e370243-f882-4718-a6db-c35c8db43c41"}},"repository":{"url":"git+https://github.com/AlexStormwood/gha-npm-publishing-demo.git","type":"git"},"_npmVersion":"11.6.2","description":"Demo project to showcase automatic NPM package publishing using the newly-required OIDC auth flow.","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/gha-npm-publishing-demo_1.0.3_1766385043779_0.7061456615694326","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"@alexstormwood/gha-npm-publishing-demo","version":"1.0.4","description":"Demo project to showcase automatic NPM package publishing using the newly-required OIDC auth flow.","main":"src/index.js","exports":{".":"./src/index.js"},"scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"repository":{"type":"git","url":"git+https://github.com/AlexStormwood/gha-npm-publishing-demo.git"},"keywords":[],"author":"","license":"ISC","type":"commonjs","bugs":{"url":"https://github.com/AlexStormwood/gha-npm-publishing-demo/issues"},"homepage":"https://github.com/AlexStormwood/gha-npm-publishing-demo#readme","gitHead":"3de12430095bdc6cfcb0214f1e94127d17e117ef","_id":"@alexstormwood/gha-npm-publishing-demo@1.0.4","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-VRsAVfpBTLmEJ8rCGWzmFawcWm6/0r6lL2F/GX/uZUq6tBiefuD4SuE/+u6RXhTa6AM85pR9+vR9kNz7jl7yVg==","shasum":"b6e2427998e671e667fa810bfaef2511617bd279","tarball":"https://registry.npmjs.org/@alexstormwood/gha-npm-publishing-demo/-/gha-npm-publishing-demo-1.0.4.tgz","fileCount":11,"unpackedSize":279866,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexstormwood%2fgha-npm-publishing-demo@1.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIE2ALbRP8e0fN0FWjBPaEeLXF/hPK0g2oSy+krc3NCXnAiAq6ZWXTTcCe/Fv3Y0iUKAk1k5LDkHZ4GthLWgu9U13rQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:0e370243-f882-4718-a6db-c35c8db43c41"}},"directories":{},"maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/gha-npm-publishing-demo_1.0.4_1766385658120_0.6352102648051037"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-22T06:09:40.872Z","modified":"2025-12-22T06:40:58.966Z","1.0.0":"2025-12-22T06:09:41.118Z","1.0.1":"2025-12-22T06:20:42.956Z","1.0.2":"2025-12-22T06:26:01.165Z","1.0.3":"2025-12-22T06:30:43.963Z","1.0.4":"2025-12-22T06:40:58.289Z"},"bugs":{"url":"https://github.com/AlexStormwood/gha-npm-publishing-demo/issues"},"license":"ISC","homepage":"https://github.com/AlexStormwood/gha-npm-publishing-demo#readme","keywords":[],"repository":{"type":"git","url":"git+https://github.com/AlexStormwood/gha-npm-publishing-demo.git"},"description":"Demo project to showcase automatic NPM package publishing using the newly-required OIDC auth flow.","maintainers":[{"name":"alexstormwood","email":"alex@bigfootds.com"}],"readme":"# @alexstormwood/gha-npm-publishing-demo\n\nDemo project to showcase automatic NPM package publishing using the newly-required OIDC auth flow.\n\n\n## Problem\n\nPublishing things can be very manual. Automation is our friend, and makes publishing easier!\n\nHowever, NPM recently made some authentication changes to their systems. Now we have to keep in mind these facts:\n\n- NPM packages are most-secure when published via a \"Trusted Publisher\" workflow, such as a git platform's continuous integration (CI) system (e.g. GitHub Actions for GitHub). Trusted publishing can only be configured on existing NPM packages, since it requires an update to a package's settings. If a package doesn't exist yet, it has no settings to edit!\n- This also means that new NPM packages cannot be created via a \"Trusted Publisher\" workflow, so you must do some manual, human-involved process to initialise a new package on NPM.\n\nWe can make granular access tokens in NPM 4 times per year and use those tokens in NPM publishing automation, and may want to do that to at least initialise a new package. But we shouldn't aim to use that for ongoing publishing of a package's new versions, as security/compliance features like package publishing provenance reports don't happen if you are _not_ using Trusted Publishing.\n\nWe can also publish packages manually, from our local command line... but that kinda defeats the whole point of even glancing at CI platforms such as GitHub Actions.\n\nIt's kinda dumb right now, but it also kinda makes sense: \n\n- Initialise a new package with human involvement.\n- Configure automation infrastructure for that new package _now that the package exists_, not _before it exists_.\n- Never manually be involved in package publishing for that specific package again.\n\n\n## Solution\n\n## Repository Setup\n\n1. Make a new repository on GitHub via its web interface, with a name like `gha-npm-publishing-demo`.\n2. Clone the project to your programming environment.\n\n### NPM Setup\n\n1. Log in to NPM, making an account if you don't have on already.\n2. Go to your account's Access Tokens page from the profile menu:\n\n![NPM profile menu.](./docs/npmprofilemenu.png)\n\n3. From the Access Tokens page, we want to generate a new token. This should be a \"granular access token\", the only type that NPM allows any more. For the token configuration:\n  - Name: Something easy for you to remember, such as `GitHub Actions Publisher 1-Day`\n  - Description: Something easy for you to remember, such as `For manually or semi-manually publishing an NPM package, such as a new package's first version.`\n  - Bypasss two-factor authentication: Tick this box. If we want to use this token in a CI workflow, then we cannot have 2FA enabled for this token.\n  - Allowed IP ranges: Skip this, as finding the IP addresses of your GitHub Actions runners can be a pain and doesn't help for creating the first version of an NPM package.\n  - Packages and Scopes Permissions: Read and Write, all packages\n  - Organizations Permissions: Only relevant if you're publishing packages to an NPM organisation. If you need it, set it to Read and Write for the relevant organisation.\n  - Expiration: Pick a custom date 1 day from the day that you're making this token.\n\n4. Save that NPM token to a text file for now, we'll need it shortly.\n\n5. Go to your repository's webpage in GitHub and open its Settings section.\n\n6. Navigate to the \"Secrets and variables\" heading in the Settings section, which should be under the \"Security\" subheading.\n\n7. Create a new secret variable in your Actions section.\n\n8. Name the secret something recognisable such as \"NPM_AUTH_TOKEN, and give it the contents of the NPM token you've just made.\n\n9. Save the secret to GitHub, and delete the local text file containing the NPM token. The token should only be used by one thing - GitHub Actions.\n\n\n![GitHub settings and secrets.](./docs/githubsecrets.png)\n\n\n\n### Project Setup\n\n\n1. Run `npm init -y` to initialise the project as a NodeJS project.\n2. Modify the `package.json` file so that its `name` field is scoped to an account or team or organisation that you have publishing permissions to in NPM, such as `\"name\": \"@alexstormwood/gha-npm-publishing-demo\",` - using your GitHub username and ensuring that your GitHub username matches your NPM username keeps this simple.\n  - (Optional) Read up on how scoped or namespaced packaging is helpful with resources like these:\n    - Karrys, L., & Thomson, E. (2023, October 23). About scopes | npm Docs. Npmjs.com. https://docs.npmjs.com/about-scopes\n3. Modify the `package.json` file so that its `main` field will match a soon-to-exist JavaScript file in a `src` directory, such as `\"main\": \"src/index.js\",`\n4. Create the `src` directory and an empty `index.js` file within that `src` directory, matching the `main` field you just edited from the `package.json`.\n5. Add some \"whatever\" code to that just-made JavaScript file. The code should export something, so we can confirm if the package functionality works as intended later. Code like this is great:\n\n  ```javascript\n  let wordOfTheDay = \"bananas\";\n  function getWotD(){\n    return wordOfTheDay;\n  }\n\n  module.exports = {\n    wordOfTheDay,\n    getWotD\t\n  }\n  ```\n6. Modify the `package.json` file so that it gains an `exports` field, with content like this:\n  ```json\n    \"exports\": {\n      \".\":\"./src/index.js\"\n    },\n  ```\n  - (Optional) Read up about the concept of \"entry points\" and what that looks like in more-complex NPM/NodeJS projects here:\n    - Modules: Packages | Node.js v25.2.1 Documentation. (2025). Nodejs.org. https://nodejs.org/api/packages.html#package-entry-points\n7. Create a `.github` directory at the root of the repository (e.g. the same level as the `src` directory, they are sibling directories).\n8. Create a `workflows` directory within the `.github` directory.\n9. Create a `cd_npm.yml` file within the `workflows` directory. This will be processed as a GitHub Actions workflow because it's a YAML file in a `.github/workflows/` directory path.\n10. Add this code to the `cd_npm.yml` file:\n\n  ```yml\n  name: CD NPM\n\n  on:\n    workflow_dispatch:\n\n  permissions:\n    id-token: write  # Required for OIDC\n    contents: read\n\n\n  jobs:\n    npm-publisher:\n      runs-on: ubuntu-latest\n      \n      steps:\n      - name: Checkout the repo\n        uses: actions/checkout@v6\n      \n      - uses: actions/setup-node@v6\n        with:\n          registry-url: 'https://registry.npmjs.org/'\n          node-version: 24\n\n      - name: Install project dependencies\n        run: npm install\n\n      - name: Build the package\n        run: npm run build --if-present\n\n      - name: Publish the package\n        if: ${{ success() }}\n        run: NODE_AUTH_TOKEN=\"\" npm publish --access public --provenance\n  ```\n\n\n\n9. Create a `first_publish_npm.yml` file within the `workflows` directory. This will be processed as a GitHub Actions workflow because it's a YAML file in a `.github/workflows/` directory path.\n10. Add this code to the `first_publish_npm.yml` file:\n\n  ```yaml\n  name: NPM Package First-Time Publish\n\n  on:\n    workflow_dispatch:\n\n  permissions:\n    contents: read\n\n\n  jobs:\n    npm-publisher:\n      runs-on: ubuntu-latest\n      \n      steps:\n      - name: Checkout the repo\n        uses: actions/checkout@v6\n      \n      - uses: actions/setup-node@v6\n        with:\n          registry-url: 'https://registry.npmjs.org/'\n          node-version: 24\n          token: ${{secrets.NPM_AUTH_TOKEN}}\n\n      - name: Install project dependencies\n        run: npm install\n\n      - name: Build the package\n        run: npm run build --if-present\n\n      - name: Publish the package\n        if: ${{ success() }}\n        run: NODE_AUTH_TOKEN=${{ secrets.NPM_AUTH_TOKEN}} npm publish --access public\n  ```\n\n  - Note the differences between the two workflows:\n    - `cd_npm` has provenance functionality, and interacts with an `id-token` permission. This is related to the OIDC stuff that we must set up soon to enable Trusted Publishing - it won't work just yet.\n    - `first_publish_npm` has no provenance or `id-token` things, but does have a `NODE_AUTH_TOKEN` value. The `cd_npm` workflow specifically sets that to a blank string, as it sometimes causes issues with OIDC functionality - but it's the key to manually publishing that first version of our new NPM package to NPM, so we need it in `first_publish_npm`!\n\n11. Save and commit your changes to your repository.\n12. Push your repository's commits to the remote repository (which already exists if you made your new repository via the GitHub website).\n\n### Enabling Trusted Publishing\n\n1. First of all: Trusted Publishing - as a system - cannot create new packages. So, navigate to the GitHub repository's Actions section, click through to your \"NPM Package First-Time Publish\" action, and click its \"Run workflow\" manual dispatcher button. Let it run - if your NPM token as configured properly and added to the repository as a secret, it will allow the \"NPM Package First-Time Publish\" action to create a new NPM package.\n\n2. Visit your new NPM package on the NPM website. Find its Settings page.\n\n3. Find the \"Trusted Publisher\" section of your NPM package's settings. Click through to your chosen provider - for this demo project, it's GitHub Actions.\n\n![NPM Trusted Publisher settings intro screen](./docs/npmtrustedpublisher001.png)\n\n4. Fill out the \"Trusted Publisher\" settings as appropriate for your repository. For example:\n\n![NPM Trusted Publisher settings form screern.](./docs/npmtrustedpublisher002.png)\n\n5. Click \"Set up connection\" on that NPM Trusted Publisher screen to finish setting things up. You should be asked for a 2FA code, too - do that.\n\n6. Jump back to your repository code and bump the version of the project in the project's `package.json` file. A singular NPM package cannot have two releases with the same version - change its right-most number to be a \"1\", so the full version field should look like: `\"version\": \"1.0.1\",`\n\n7. Save, commit, and push the local changes to the remote repository.\n\n8. Jump back to your GitHub repository's Actions section, and find the \"CD NPM\" action. From there, click its \"Run workflow\" manual dispatcher button. Let it run.\n\n### Confirming The Package Works\n\n1. Make a new NodeJS project and do the usual `npm init` setup stuff.\n\n2. Run `npm install @alexstormwood/gha-npm-publishing-demo` or the equivalent using your own published package.\n\n3. Write this code, run this code, enjoy:\n\n```javascript\nconst ghaPackage = require(\"@alexstormwood/gha-npm-publishing-demo\");\n\nconsole.log(ghaPackage.getWotD());\nconsole.log(ghaPackage.wordOfTheDay());\n```\n\n## Common Problems When Setting This Up\n\n- Older versions of NodeJS in the GitHub Actions runners don't have the right NPM to deal with this new OIDC way of doing things. Specify NodeJS 24 when setting up NodeJS in the job runners.\n- Trusted Publisher configuration is specific - capitalisation and special characters matter! No \"@\" in organisation or usernames, no character cases that don't exactly match the target name.\n- That package version property in the `package.json` file must always be some semver increment above the previously-published version of the package.\n\n\n## More-Real Usage\n\nHonestly, this just comes down to triggers for your workflows and things like CI/CD environment configuration. Go nuts. That stuff is beyond the scope of this repository. Look at other open-source projects to see what type of CI/CD they implement, and what type of events or triggers they use to publish their packages.","readmeFilename":"README.md"}