diff --git a/canary-publish/README.ko.md b/canary-publish/README.ko.md index ec38095..bee15dc 100644 --- a/canary-publish/README.ko.md +++ b/canary-publish/README.ko.md @@ -43,7 +43,6 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} # (필수) GitHub API 인증 토큰. 필요시 사용자 PAT로 대체 가능 npm_tag: canary # (선택) 배포에 사용할 npm 태그 (예: canary, beta 등) - npm_token: ${{ secrets.NPM_TOKEN }} # (필수) npm publish를 위한 인증 토큰 publish_script: pnpm run deploy:canary # (필수) Canary 배포를 실행할 스크립트 명령어 packages_dir: packages # (선택) 변경 감지에 사용할 패키지 디렉터리 (기본값: packages,share) excludes: ".turbo,.github" # (선택) 변경 감지에서 제외할 파일/디렉터리 목록 (쉼표로 구분) @@ -51,6 +50,7 @@ jobs: dry_run: false # (선택) true면 실제 배포 없이 시뮬레이션만 수행 language: 'en' # (선택) 메시지 언어 설정 (en, ko 등) create_release: false # (선택) true면 Canary 배포 후 GitHub Release 자동 생성 + provenance: true # (선택) provenance 생성 활성화 (npm CLI 11.5.1+ 필요) ``` ### ⚠️ `create_release: true` 사용 시 주의사항 @@ -108,6 +108,89 @@ jobs: **위 설정이 누락되면 릴리즈 생성이 실패할 수 있습니다. 반드시 확인해 주세요!** +## NPM OIDC 신뢰할 수 있는 게시 + +이 액션은 NPM의 OIDC 기반 신뢰할 수 있는 게시를 사용합니다. NPM 토큰을 시크릿으로 저장할 필요가 없으며, 워크플로우별 단기 자격 증명을 사용하여 더 나은 보안을 제공합니다. + +### 사전 요구사항 + +1. **NPM CLI 버전**: npm CLI v11.5.1 이상 필요 +2. **GitHub Actions 러너**: GitHub 호스트 러너 사용 필수 +3. **NPM 패키지 설정**: npmjs.com에서 신뢰할 수 있는 게시자 설정 필요 + +### 설정 방법 + +1. **npmjs.com에서 신뢰할 수 있는 게시자 설정**: + - npmjs.com에서 패키지 설정으로 이동 + - "Publishing access" → "Trusted publishers"로 이동 + - 다음 정보로 새 신뢰할 수 있는 게시자 추가: + - Organization/User: GitHub 조직 또는 사용자 이름 + - Repository: 저장소 이름 + - Workflow filename: 워크플로우 파일 이름 (예: `canary-publish.yml`) + - Environment name: (선택) GitHub 환경을 사용하는 경우 + +2. **워크플로우 업데이트**: + - `id-token: write` 권한 추가 + - `npm_token` 입력 제거 (또는 비워두기) + - npm CLI 버전이 11.5.1+ 인지 확인 + +OIDC를 사용하는 워크플로우 예시: + +```yaml +name: changeset canary publish + +on: + issue_comment: + types: + - created + +permissions: + id-token: write # OIDC에 필수 + contents: write # 릴리즈 생성에 필수 + pull-requests: write # PR 코멘트에 필수 + +jobs: + canary: + if: ${{ github.event.issue.pull_request && (github.event.comment.body == 'canary-publish' || github.event.comment.body == '/canary-publish')}} + runs-on: ubuntu-latest + steps: + - name: Get PR branch name + id: get_branch + run: | + PR=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" ${{ github.event.issue.pull_request.url }}) + echo "::set-output name=branch::$(echo $PR | jq -r '.head.ref')" + + - name: Checkout Repo + uses: actions/checkout@v3 + with: + ref: ${{ steps.get_branch.outputs.branch }} + + - name: Setup Node with latest npm + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Canary Publish + uses: NaverPayDev/changeset-actions/canary-publish@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + npm_tag: canary + publish_script: pnpm run deploy:canary + packages_dir: packages + provenance: true +``` + +### OIDC의 장점 + +- NPM 토큰을 생성, 저장, 갱신할 필요 없음 +- 자동 provenance 증명 +- 토큰 유출 위험 감소 +- 더 나은 감사 추적 + ## 실행 결과 ![example](./src/assets/example.png) diff --git a/canary-publish/README.md b/canary-publish/README.md index 25b6695..bdec606 100644 --- a/canary-publish/README.md +++ b/canary-publish/README.md @@ -43,7 +43,6 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} # (Required) GitHub API token for authentication. Use a user PAT if necessary. npm_tag: canary # (Optional) The npm tag to use for deployment (e.g., canary, beta). - npm_token: ${{ secrets.NPM_TOKEN }} # (Required) Token used for npm publishing. publish_script: pnpm run deploy:canary # (Required) Script command to execute the canary deployment. packages_dir: packages # (Optional) Directory containing packages to check for changes (default: packages,share). excludes: ".turbo,.github" # (Optional) Files or directories to exclude from change detection (comma-separated). @@ -51,6 +50,7 @@ jobs: dry_run: false # (Optional) If true, performs a dry run without publishing. language: 'en' # (Optional) Language for output messages (e.g., en, ko). create_release: false # (Optional) If true, creates a GitHub Release after canary publishing. + provenance: true # (Optional) Enable provenance statements (requires npm CLI 11.5.1+) ``` ### ⚠️ Important Notes for `create_release: true` @@ -108,6 +108,89 @@ jobs: **If any of these are missing, the release creation step may fail.** +## NPM OIDC Trusted Publishing + +This action uses NPM's OIDC-based trusted publishing, which eliminates the need for storing NPM tokens as secrets. This provides better security by using short-lived, workflow-specific credentials. + +### Prerequisites + +1. **NPM CLI Version**: Requires npm CLI v11.5.1 or later +2. **GitHub Actions Runner**: Must use GitHub-hosted runners +3. **NPM Package Configuration**: Configure trusted publishers on npmjs.com + +### Setup Steps + +1. **Configure Trusted Publisher on npmjs.com**: + - Go to your package settings on npmjs.com + - Navigate to "Publishing access" → "Trusted publishers" + - Add a new trusted publisher with: + - Organization/User: Your GitHub organization or username + - Repository: Your repository name + - Workflow filename: Your workflow file name (e.g., `canary-publish.yml`) + - Environment name: (Optional) If using GitHub environments + +2. **Update your workflow**: + - Add `id-token: write` permission + - Remove `npm_token` input (or leave it empty) + - Ensure npm CLI version is 11.5.1+ + +Example workflow with OIDC: + +```yaml +name: changeset canary publish + +on: + issue_comment: + types: + - created + +permissions: + id-token: write # Required for OIDC + contents: write # Required for creating releases + pull-requests: write # Required for PR comments + +jobs: + canary: + if: ${{ github.event.issue.pull_request && (github.event.comment.body == 'canary-publish' || github.event.comment.body == '/canary-publish')}} + runs-on: ubuntu-latest + steps: + - name: Get PR branch name + id: get_branch + run: | + PR=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" ${{ github.event.issue.pull_request.url }}) + echo "::set-output name=branch::$(echo $PR | jq -r '.head.ref')" + + - name: Checkout Repo + uses: actions/checkout@v3 + with: + ref: ${{ steps.get_branch.outputs.branch }} + + - name: Setup Node with latest npm + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Install Dependencies + run: pnpm install --frozen-lockfile + + - name: Canary Publish + uses: NaverPayDev/changeset-actions/canary-publish@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + npm_tag: canary + publish_script: pnpm run deploy:canary + packages_dir: packages + provenance: true +``` + +### Benefits of OIDC + +- No need to create, store, or rotate NPM tokens +- Automatic provenance attestations +- Reduced risk of token exfiltration +- Better audit trail + ## Execution Results ![example](./src/assets/example.png) diff --git a/canary-publish/action.yml b/canary-publish/action.yml index 22b6d01..fa607ba 100644 --- a/canary-publish/action.yml +++ b/canary-publish/action.yml @@ -11,9 +11,6 @@ inputs: description: 'npm tag' default: canary required: false - npm_token: - description: 'npm token' - required: true publish_script: description: 'canary deploy script' required: true @@ -41,3 +38,7 @@ inputs: description: 'language for release note, comment etc.' required: false default: 'en' + provenance: + description: 'Enable provenance statements (requires npm CLI 11.5.1+, uses OIDC if available)' + required: false + default: 'true' diff --git a/canary-publish/dist/index.js b/canary-publish/dist/index.js index f93b7fe..c7e6ffd 100644 --- a/canary-publish/dist/index.js +++ b/canary-publish/dist/index.js @@ -53566,33 +53566,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge Object.defineProperty(exports, "__esModule", ({ value: true })); exports.setNpmRc = setNpmRc; const core = __importStar(__nccwpck_require__(6108)); -const fs = __importStar(__nccwpck_require__(77)); function setNpmRc() { return __awaiter(this, void 0, void 0, function* () { - // 입력값에서 npm_token 가져오기 - const npmToken = core.getInput('npm_token'); - process.env.NPM_TOKEN = npmToken; // 환경변수로 설정 - core.info('No changesets found, attempting to publish any unpublished packages to npm'); - const userNpmrcPath = `${process.env.HOME}/.npmrc`; - if (fs.existsSync(userNpmrcPath)) { - core.info('Found existing user .npmrc file'); - const userNpmrcContent = yield fs.readFile(userNpmrcPath, 'utf8'); - const authLine = userNpmrcContent.split('\n').find((line) => { - // check based on https://github.com/npm/cli/blob/8f8f71e4dd5ee66b3b17888faad5a7bf6c657eed/test/lib/adduser.js#L103-L105 - return /^\s*\/\/registry\.npmjs\.org\/:[_-]authToken=/i.test(line); - }); - if (authLine) { - core.info('Found existing auth token for the npm registry in the user .npmrc file'); - } - else { - core.info("Didn't find existing auth token for the npm registry in the user .npmrc file, creating one"); - fs.appendFileSync(userNpmrcPath, `\n//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}\n`); - } - } - else { - core.info('No user .npmrc file found, creating one'); - fs.writeFileSync(userNpmrcPath, `//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}\n`); - } + core.info('Using OIDC trusted publishing for npm authentication'); + core.info('Ensure npm CLI 11.5.1+ is installed and id-token: write permission is set'); }); } diff --git a/canary-publish/src/utils/npm.ts b/canary-publish/src/utils/npm.ts index 825ca34..56c7191 100644 --- a/canary-publish/src/utils/npm.ts +++ b/canary-publish/src/utils/npm.ts @@ -1,29 +1,6 @@ import * as core from '@actions/core' -import * as fs from 'fs-extra' export async function setNpmRc() { - // 입력값에서 npm_token 가져오기 - const npmToken = core.getInput('npm_token') - process.env.NPM_TOKEN = npmToken // 환경변수로 설정 - - core.info('No changesets found, attempting to publish any unpublished packages to npm') - - const userNpmrcPath = `${process.env.HOME}/.npmrc` - if (fs.existsSync(userNpmrcPath)) { - core.info('Found existing user .npmrc file') - const userNpmrcContent = await fs.readFile(userNpmrcPath, 'utf8') - const authLine = userNpmrcContent.split('\n').find((line) => { - // check based on https://github.com/npm/cli/blob/8f8f71e4dd5ee66b3b17888faad5a7bf6c657eed/test/lib/adduser.js#L103-L105 - return /^\s*\/\/registry\.npmjs\.org\/:[_-]authToken=/i.test(line) - }) - if (authLine) { - core.info('Found existing auth token for the npm registry in the user .npmrc file') - } else { - core.info("Didn't find existing auth token for the npm registry in the user .npmrc file, creating one") - fs.appendFileSync(userNpmrcPath, `\n//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}\n`) - } - } else { - core.info('No user .npmrc file found, creating one') - fs.writeFileSync(userNpmrcPath, `//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}\n`) - } + core.info('Using OIDC trusted publishing for npm authentication') + core.info('Ensure npm CLI 11.5.1+ is installed and id-token: write permission is set') } diff --git a/publish/README.ko.md b/publish/README.ko.md index c9e2663..5e10e60 100644 --- a/publish/README.ko.md +++ b/publish/README.ko.md @@ -30,7 +30,6 @@ jobs: - uses: NaverPayDev/changeset-actions/publish@main with: github_token: ${{ secrets.GITHUB_TOKEN }} # 필요하면 user의 PAT을 넣어주세요. - npm_token: ${{ secrets.NPM_TOKEN }} # npm 배포시 필요한 publish token 을 넣어주세요 publish_script: pnpm run deploy # 배포 실행 script 를 넣어주세요 git_username: npay-fe-bot # 버전업 pr 생성시 설정할 github username 을 넣어주세요 git_email: npay.fe.bot@navercorp.com # 버전업 pr 생성시 설정할 github email 을 넣어주세요 @@ -38,8 +37,85 @@ jobs: commit_message: 📦 bump changed packages version # 버전업 pr 생성시 설정할 commit 메시지를 넣어주세요 create_github_release_tag: true # release tag 생성여부를 넣어주세요 formatting_script: pnpm run markdownlint:fix # 생성되는 md 파일의 formatting이 필요하다면 추가해주세요 + provenance: true # (선택) provenance 생성 활성화 (npm CLI 11.5.1+ 필요) ``` +## NPM OIDC 신뢰할 수 있는 게시 + +이 액션은 NPM의 OIDC 기반 신뢰할 수 있는 게시를 사용합니다. NPM 토큰을 시크릿으로 저장할 필요가 없으며, 워크플로우별 단기 자격 증명을 사용하여 더 나은 보안을 제공합니다. + +### 사전 요구사항 + +1. **NPM CLI 버전**: npm CLI v11.5.1 이상 필요 +2. **GitHub Actions 러너**: GitHub 호스트 러너 사용 필수 +3. **NPM 패키지 설정**: npmjs.com에서 신뢰할 수 있는 게시자 설정 필요 + +### 설정 방법 + +1. **npmjs.com에서 신뢰할 수 있는 게시자 설정**: + - npmjs.com에서 패키지 설정으로 이동 + - "Publishing access" → "Trusted publishers"로 이동 + - 다음 정보로 새 신뢰할 수 있는 게시자 추가: + - Organization/User: GitHub 조직 또는 사용자 이름 + - Repository: 저장소 이름 + - Workflow filename: 워크플로우 파일 이름 (예: `publish.yml`) + - Environment name: (선택) GitHub 환경을 사용하는 경우 + +2. **워크플로우 업데이트**: + - `id-token: write` 권한 추가 + - `npm_token` 입력 제거 (또는 비워두기) + - npm CLI 버전이 11.5.1+ 인지 확인 + +OIDC를 사용하는 워크플로우 예시: + +```yaml +name: changeset-publish + +on: + push: + branches: + - main + +permissions: + id-token: write # OIDC에 필수 + contents: write # 릴리즈 생성에 필수 + pull-requests: write # PR 생성에 필수 + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + detectAdd: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + + - name: Setup Node with latest npm + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - uses: NaverPayDev/changeset-actions/publish@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_script: pnpm run deploy + git_username: npay-fe-bot + git_email: npay.fe.bot@navercorp.com + pr_title: 🚀 version changed packages + commit_message: 📦 bump changed packages version + create_github_release_tag: true + provenance: true +``` + +### OIDC의 장점 + +- NPM 토큰을 생성, 저장, 갱신할 필요 없음 +- 자동 provenance 증명 +- 토큰 유출 위험 감소 +- 더 나은 감사 추적 + ## 실행 결과 ![example](./src/assets/pr.png) diff --git a/publish/README.md b/publish/README.md index a08a5c1..b2aeac5 100644 --- a/publish/README.md +++ b/publish/README.md @@ -29,7 +29,6 @@ jobs: - uses: NaverPayDev/changeset-actions/actions/publish@main with: github_token: ${{ secrets.GITHUB_TOKEN }} # Add user PAT if necessary - npm_token: ${{ secrets.NPM_TOKEN }} # Token required for npm publishing publish_script: pnpm run deploy # Script to execute the deployment git_username: npay-fe-bot # GitHub username for version bump PR creation git_email: npay.fe.bot@navercorp.com # GitHub email for version bump PR creation @@ -37,8 +36,85 @@ jobs: commit_message: 📦 bump changed packages version # Commit message for version bump create_github_release_tag: true # Whether to create a release tag formatting_script: pnpm run markdownlint:fix # Add if formatting the generated markdown files is required + provenance: true # (Optional) Enable provenance statements (requires npm CLI 11.5.1+) ``` +## NPM OIDC Trusted Publishing + +This action uses NPM's OIDC-based trusted publishing, which eliminates the need for storing NPM tokens as secrets. This provides better security by using short-lived, workflow-specific credentials. + +### Prerequisites + +1. **NPM CLI Version**: Requires npm CLI v11.5.1 or later +2. **GitHub Actions Runner**: Must use GitHub-hosted runners +3. **NPM Package Configuration**: Configure trusted publishers on npmjs.com + +### Setup Steps + +1. **Configure Trusted Publisher on npmjs.com**: + - Go to your package settings on npmjs.com + - Navigate to "Publishing access" → "Trusted publishers" + - Add a new trusted publisher with: + - Organization/User: Your GitHub organization or username + - Repository: Your repository name + - Workflow filename: Your workflow file name (e.g., `publish.yml`) + - Environment name: (Optional) If using GitHub environments + +2. **Update your workflow**: + - Add `id-token: write` permission + - Remove `npm_token` input (or leave it empty) + - Ensure npm CLI version is 11.5.1+ + +Example workflow with OIDC: + +```yaml +name: changeset-publish + +on: + push: + branches: + - main + +permissions: + id-token: write # Required for OIDC + contents: write # Required for creating releases + pull-requests: write # Required for PR creation + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + detectAdd: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + + - name: Setup Node with latest npm + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - uses: NaverPayDev/changeset-actions/actions/publish@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_script: pnpm run deploy + git_username: npay-fe-bot + git_email: npay.fe.bot@navercorp.com + pr_title: 🚀 version changed packages + commit_message: 📦 bump changed packages version + create_github_release_tag: true + provenance: true +``` + +### Benefits of OIDC + +- No need to create, store, or rotate NPM tokens +- Automatic provenance attestations +- Reduced risk of token exfiltration +- Better audit trail + ## Execution Results ![example](./src/assets/pr.png) diff --git a/publish/action.yml b/publish/action.yml index 5a7d247..b6f6240 100644 --- a/publish/action.yml +++ b/publish/action.yml @@ -7,9 +7,6 @@ inputs: github_token: description: 'github token to use for the action' required: true - npm_token: - description: 'npm publish token' - required: true publish_script: description: '배포 script' required: false @@ -36,3 +33,7 @@ inputs: formatting_script: description: 'add a formatting script when needing to format the .changeset/*.md files' required: false + provenance: + description: 'Enable provenance statements (requires npm CLI 11.5.1+, uses OIDC if available)' + required: false + default: 'true' diff --git a/publish/dist/index.js b/publish/dist/index.js index 801b4f1..77d5a15 100644 --- a/publish/dist/index.js +++ b/publish/dist/index.js @@ -64370,31 +64370,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge Object.defineProperty(exports, "__esModule", ({ value: true })); exports.setNpmRc = setNpmRc; const core = __importStar(__nccwpck_require__(6108)); -const fs = __importStar(__nccwpck_require__(20077)); function setNpmRc() { return __awaiter(this, void 0, void 0, function* () { - core.info('No changesets found, attempting to publish any unpublished packages to npm'); - const npmToken = core.getInput('npm_token'); - const userNpmrcPath = `${process.env.HOME}/.npmrc`; - if (fs.existsSync(userNpmrcPath)) { - core.info('Found existing user .npmrc file'); - const userNpmrcContent = yield fs.readFile(userNpmrcPath, 'utf8'); - const authLine = userNpmrcContent.split('\n').find((line) => { - // check based on https://github.com/npm/cli/blob/8f8f71e4dd5ee66b3b17888faad5a7bf6c657eed/test/lib/adduser.js#L103-L105 - return /^\s*\/\/registry\.npmjs\.org\/:[_-]authToken=/i.test(line); - }); - if (authLine) { - core.info('Found existing auth token for the npm registry in the user .npmrc file'); - } - else { - core.info("Didn't find existing auth token for the npm registry in the user .npmrc file, creating one"); - fs.appendFileSync(userNpmrcPath, `\n//registry.npmjs.org/:_authToken=${npmToken}\n`); - } - } - else { - core.info('No user .npmrc file found, creating one'); - fs.writeFileSync(userNpmrcPath, `//registry.npmjs.org/:_authToken=${npmToken}\n`); - } + core.info('Using OIDC trusted publishing for npm authentication'); + core.info('Ensure npm CLI 11.5.1+ is installed and id-token: write permission is set'); }); } diff --git a/publish/src/utils/npm.ts b/publish/src/utils/npm.ts index cea4dc4..56c7191 100644 --- a/publish/src/utils/npm.ts +++ b/publish/src/utils/npm.ts @@ -1,27 +1,6 @@ import * as core from '@actions/core' -import * as fs from 'fs-extra' export async function setNpmRc() { - core.info('No changesets found, attempting to publish any unpublished packages to npm') - - const npmToken = core.getInput('npm_token') - - const userNpmrcPath = `${process.env.HOME}/.npmrc` - if (fs.existsSync(userNpmrcPath)) { - core.info('Found existing user .npmrc file') - const userNpmrcContent = await fs.readFile(userNpmrcPath, 'utf8') - const authLine = userNpmrcContent.split('\n').find((line) => { - // check based on https://github.com/npm/cli/blob/8f8f71e4dd5ee66b3b17888faad5a7bf6c657eed/test/lib/adduser.js#L103-L105 - return /^\s*\/\/registry\.npmjs\.org\/:[_-]authToken=/i.test(line) - }) - if (authLine) { - core.info('Found existing auth token for the npm registry in the user .npmrc file') - } else { - core.info("Didn't find existing auth token for the npm registry in the user .npmrc file, creating one") - fs.appendFileSync(userNpmrcPath, `\n//registry.npmjs.org/:_authToken=${npmToken}\n`) - } - } else { - core.info('No user .npmrc file found, creating one') - fs.writeFileSync(userNpmrcPath, `//registry.npmjs.org/:_authToken=${npmToken}\n`) - } + core.info('Using OIDC trusted publishing for npm authentication') + core.info('Ensure npm CLI 11.5.1+ is installed and id-token: write permission is set') }