diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 00000000..a4818be1 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,11 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can find the +full documentation for it [in our repository](https://github.com/changesets/changesets). + +We have a quick list of common questions to get you started engaging with this project in +[our docs](https://github.com/changesets/changesets/blob/main/docs/common-questions.md). We also have a +[introductory guide](https://github.com/changesets/changesets/blob/main/docs/intro-to-using-changesets.md) +and information about how we [version things](https://github.com/changesets/changesets/blob/main/docs/versioning-policy.md) +for further context. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 00000000..852554ea --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": [ + "@changesets/changelog-github", + { "repo": "kjldev/purview-telemetry-sourcegenerator" } + ], + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.changeset/embedded-attribute-definition.md b/.changeset/embedded-attribute-definition.md new file mode 100644 index 00000000..03781070 --- /dev/null +++ b/.changeset/embedded-attribute-definition.md @@ -0,0 +1,7 @@ +--- +"purview-telemetry-sourcegenerator": minor +--- + +Use `AddEmbeddedAttributeDefinition` to inject `Microsoft.CodeAnalysis.EmbeddedAttribute` via Roslyn's official API, and apply `[global::Microsoft.CodeAnalysis.Embedded]` to all generated marker attribute types. + +This ensures generated marker types are invisible to IDE tooling and consumer code, eliminating spurious symbol conflicts. Requires Roslyn 4.14.0+ (ships with Visual Studio 2022 17.14+ and .NET 10 SDK). diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 433a94ff..687840c2 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -9,13 +9,7 @@ on: branches: - main paths: - - .github/workflows/cd.yml - - Justfile - - global.json - package.json - - benchmarks/** - - samples/** - - src/** jobs: release: @@ -35,36 +29,8 @@ jobs: with: fetch-depth: 0 - - id: release-trigger - name: Determine whether to publish a release - env: - EVENT_NAME: ${{ github.event_name }} - BEFORE_SHA: ${{ github.event.before }} - CURRENT_SHA: ${{ github.sha }} - shell: bash - run: | - set -euo pipefail - - if [[ "${EVENT_NAME}" == "workflow_dispatch" ]]; then - echo "should_release=true" >> "${GITHUB_OUTPUT}" - exit 0 - fi - - if [[ "${BEFORE_SHA}" == "0000000000000000000000000000000000000000" ]]; then - changed_files="$(git show --name-only --pretty='' "${CURRENT_SHA}")" - else - changed_files="$(git diff --name-only "${BEFORE_SHA}" "${CURRENT_SHA}")" - fi - - if grep -Fxq "package.json" <<< "${changed_files}"; then - echo "should_release=true" >> "${GITHUB_OUTPUT}" - else - echo "should_release=false" >> "${GITHUB_OUTPUT}" - fi - - id: version name: Read release version - if: ${{ steps.release-trigger.outputs.should_release == 'true' }} shell: bash run: | set -euo pipefail @@ -79,74 +45,62 @@ jobs: echo "prerelease=false" >> "${GITHUB_OUTPUT}" fi - - name: Block duplicate version - if: ${{ steps.release-trigger.outputs.should_release == 'true' }} + - id: release-check + name: Check if release already exists env: - GITHUB_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ github.token }} RELEASE_TAG: ${{ steps.version.outputs.tag }} shell: bash run: | set -euo pipefail - if git ls-remote --exit-code --tags origin "refs/tags/${RELEASE_TAG}" > /dev/null 2>&1; then - echo "::error::Tag ${RELEASE_TAG} already exists on origin. Refusing to replace an existing release version." - exit 1 - fi - - release_status="$( - curl -sS \ - -o release.json \ - -w "%{http_code}" \ - -H "Authorization: Bearer ${GITHUB_TOKEN}" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${RELEASE_TAG}" - )" - - if [[ "${release_status}" == "200" ]]; then - echo "::error::Release ${RELEASE_TAG} already exists. Refusing to replace an existing release version." - cat release.json - exit 1 - fi - - if [[ "${release_status}" != "404" ]]; then - echo "::error::Unexpected GitHub API response while checking ${RELEASE_TAG} (HTTP ${release_status})." - cat release.json - exit 1 + if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" &>/dev/null; then + echo "::notice::Release ${RELEASE_TAG} already exists — skipping." + echo "should_release=false" >> "${GITHUB_OUTPUT}" + else + echo "should_release=true" >> "${GITHUB_OUTPUT}" fi - rm -f release.json - - name: Set up .NET + if: ${{ steps.release-check.outputs.should_release == 'true' }} uses: actions/setup-dotnet@v5 with: global-json-file: ./global.json - name: Restore dotnet tools + if: ${{ steps.release-check.outputs.should_release == 'true' }} run: dotnet tool restore - name: CSharpier format check + if: ${{ steps.release-check.outputs.should_release == 'true' }} run: dotnet csharpier check . - name: Restore main solution + if: ${{ steps.release-check.outputs.should_release == 'true' }} run: dotnet restore ${{ env.MAIN_SOLUTION }} - name: Build main solution + if: ${{ steps.release-check.outputs.should_release == 'true' }} run: dotnet build ${{ env.MAIN_SOLUTION }} --no-restore --configuration ${{ env.CONFIGURATION }} - name: Test main solution - run: dotnet test --solution ${{ env.MAIN_SOLUTION }} --no-build --configuration ${{ env.CONFIGURATION }} --verbosity normal + if: ${{ steps.release-check.outputs.should_release == 'true' }} + run: dotnet test ${{ env.MAIN_SOLUTION }} --no-build --configuration ${{ env.CONFIGURATION }} --verbosity normal - name: Restore sample solution + if: ${{ steps.release-check.outputs.should_release == 'true' }} run: dotnet restore ${{ env.SAMPLE_SOLUTION }} - name: Build sample solution + if: ${{ steps.release-check.outputs.should_release == 'true' }} run: dotnet build ${{ env.SAMPLE_SOLUTION }} --no-restore --configuration ${{ env.CONFIGURATION }} - name: Test sample solution - run: dotnet test --solution ${{ env.SAMPLE_SOLUTION }} --no-build --configuration ${{ env.CONFIGURATION }} --verbosity normal + if: ${{ steps.release-check.outputs.should_release == 'true' }} + run: dotnet test ${{ env.SAMPLE_SOLUTION }} --no-build --configuration ${{ env.CONFIGURATION }} --verbosity normal - name: Pack NuGet artifacts - if: ${{ steps.release-trigger.outputs.should_release == 'true' }} + if: ${{ steps.release-check.outputs.should_release == 'true' }} env: PACKAGE_VERSION: ${{ steps.version.outputs.value }} shell: bash @@ -165,20 +119,47 @@ jobs: "/p:RepositoryCommit=${GITHUB_SHA}" \ "/p:COPYRIGHT_YEAR=$(date -u +%Y)" - - name: Create GitHub release - if: ${{ steps.release-trigger.outputs.should_release == 'true' }} - uses: softprops/action-gh-release@v3 - with: - tag_name: ${{ steps.version.outputs.tag }} - name: ${{ steps.version.outputs.tag }} - target_commitish: ${{ github.sha }} - generate_release_notes: true - prerelease: ${{ steps.version.outputs.prerelease }} - fail_on_unmatched_files: true - files: | - ./artifacts/nuget/*.nupkg + - name: Publish GitHub Release + if: ${{ steps.release-check.outputs.should_release == 'true' }} + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ steps.version.outputs.tag }} + PRERELEASE: ${{ steps.version.outputs.prerelease }} + shell: bash + run: | + set -euo pipefail + + # On any error: delete the draft release and its tag so the next run starts clean. + # Draft releases are never immutable, so this always succeeds. + cleanup() { + echo "::warning::Release failed — cleaning up draft and tag for ${RELEASE_TAG}..." + gh release delete "${RELEASE_TAG}" --yes --cleanup-tag 2>/dev/null || true + } + trap cleanup ERR + + PRERELEASE_FLAG="" + if [[ "${PRERELEASE}" == "true" ]]; then + PRERELEASE_FLAG="--prerelease" + fi + + # Create as draft first — draft releases are never immutable, so assets + # can be freely attached. The release becomes immutable only after --draft=false. + gh release create "${RELEASE_TAG}" \ + --draft \ + --title "${RELEASE_TAG}" \ + --generate-notes \ + ${PRERELEASE_FLAG} \ + ./artifacts/nuget/*.nupkg \ ./artifacts/nuget/*.snupkg + # Publish: marks as latest and becomes immutable. Any retry will clean up above. + gh release edit "${RELEASE_TAG}" \ + --draft=false \ + --latest + + echo "✅ Release ${RELEASE_TAG} published successfully." + - name: Skip release publish - if: ${{ steps.release-trigger.outputs.should_release != 'true' }} - run: echo "package.json did not change on this push, so release publishing was skipped." + if: ${{ steps.release-check.outputs.should_release != 'true' }} + run: echo "Release ${{ steps.version.outputs.tag }} already exists — skipping." + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b933a7dd..df34252c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,7 @@ jobs: run: dotnet build ${{ env.SOLUTION }} --no-restore --configuration ${{ env.CONFIGURATION }} - name: Run tests - run: dotnet test --solution ${{ env.SOLUTION }} --no-build --verbosity normal --configuration ${{ env.CONFIGURATION }} + run: dotnet test ${{ env.SOLUTION }} --no-build --verbosity normal --configuration ${{ env.CONFIGURATION }} build-and-test-samples: name: Build and Test Samples @@ -78,7 +78,51 @@ jobs: run: dotnet build ${{ env.SOLUTION }} --no-restore --configuration ${{ env.CONFIGURATION }} - name: Run tests - run: dotnet test --solution ${{ env.SOLUTION }} --no-build --verbosity normal --configuration ${{ env.CONFIGURATION }} + run: dotnet test ${{ env.SOLUTION }} --no-build --verbosity normal --configuration ${{ env.CONFIGURATION }} + + changeset-check: + name: Changeset Check + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Check for changeset + env: + HEAD_REF: ${{ github.head_ref }} + PR_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} + shell: bash + run: | + set -euo pipefail + + # Version Packages PRs are created by the release bot — no changeset needed + if [[ "${HEAD_REF}" == changeset-release/* ]]; then + echo "Version Packages PR — changeset check skipped." + exit 0 + fi + + # PRs labelled skip-changeset are explicitly opted out (docs-only, CI-only, etc.) + if echo "${PR_LABELS}" | grep -q '"skip-changeset"'; then + echo "skip-changeset label present — changeset check skipped." + exit 0 + fi + + git fetch origin main --depth=1 + + changed_files="$(git diff --name-only origin/main...HEAD)" + changeset_files="$(echo "${changed_files}" | grep -E '^\.changeset/[^/]+\.md$' | grep -v 'README\.md' || true)" + + if [[ -z "${changeset_files}" ]]; then + echo "::error::No changeset file found." + echo "::error::Run 'just changeset' to create one, or add the 'skip-changeset' label if this PR needs no release note." + exit 1 + fi + + echo "✅ Changeset found:" + echo "${changeset_files}" ci-gate: name: CI Gate @@ -88,6 +132,7 @@ jobs: - format-check - build-and-test - build-and-test-samples + - changeset-check steps: - name: Verify upstream jobs succeeded @@ -95,6 +140,7 @@ jobs: FORMAT_CHECK_RESULT: ${{ needs['format-check'].result }} BUILD_AND_TEST_RESULT: ${{ needs['build-and-test'].result }} BUILD_AND_TEST_SAMPLES_RESULT: ${{ needs['build-and-test-samples'].result }} + CHANGESET_CHECK_RESULT: ${{ needs['changeset-check'].result }} shell: bash run: | set -euo pipefail @@ -103,6 +149,7 @@ jobs: ["Format Check"]="${FORMAT_CHECK_RESULT}" ["Build and Test"]="${BUILD_AND_TEST_RESULT}" ["Build and Test Samples"]="${BUILD_AND_TEST_SAMPLES_RESULT}" + ["Changeset Check"]="${CHANGESET_CHECK_RESULT}" ) failures=0 diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml new file mode 100644 index 00000000..9fb1471a --- /dev/null +++ b/.github/workflows/release-pr.yml @@ -0,0 +1,44 @@ +name: Release PR + +permissions: + contents: write + pull-requests: write + +on: + push: + branches: + - main + +jobs: + release-pr: + name: Create or update Release PR + runs-on: ubuntu-latest + + steps: + - name: Generate GitHub App token + id: app-token + if: ${{ secrets.APP_ID != '' }} + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + token: ${{ steps.app-token.outputs.token || secrets.CHANGESET_TOKEN }} + + - name: Set up Bun + uses: oven-sh/setup-bun@v2 + + - name: Install dependencies + run: bun install + + - name: Create or update Release PR + uses: changesets/action@v1 + with: + version: bun run version-packages + createGithubReleases: false + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token || secrets.CHANGESET_TOKEN }} diff --git a/.wiki b/.wiki index c6b11775..39c51bb2 160000 --- a/.wiki +++ b/.wiki @@ -1 +1 @@ -Subproject commit c6b11775ae23238edbdef596fb9d6969aacd4801 +Subproject commit 39c51bb24870ece001eb8b6af9b643d3f592e754 diff --git a/Justfile b/Justfile index 16b2b12b..48b700e5 100644 --- a/Justfile +++ b/Justfile @@ -38,17 +38,15 @@ test-s: @Write-Host "Running tests for {{ BLUE }}{{ sample_solution_file }}{{ NORMAL }} with {{ YELLOW }}{{ configuration }}{{ NORMAL }}..." @dotnet test --solution "{{ sample_solution_file }}" --configuration "{{ configuration }}" -# Creates a new release (final or prerelease) using bun +# Creates a new changeset to describe the changes in your current branch (requires bun) [group('Versioning and Release')] -release-final: - @Write-Host "Committing the changes and creating a new release..." - @bun release +changeset: + @bun changeset -# Creates a new prerelease using bun +# Shows pending changesets and what version bump they imply (requires bun) [group('Versioning and Release')] -release-pre: - @Write-Host "Committing the changes and creating a new release..." - @bun release -- --prerelease prerelease +changeset-status: + @bun changeset status # Packs the source generator into a NuGet package and outputs it to the specified folder [group('Build and Test')] diff --git a/README.md b/README.md index c52d9891..b7363174 100644 --- a/README.md +++ b/README.md @@ -279,3 +279,9 @@ public enum NamingConvention ``` **Recommendation:** Use `NamingConvention.OpenTelemetry` (default) for new projects. Only use `Legacy` if you need exact v3 compatibility. + +## Contributing + +Contributions are welcome! See the [Contributing guide](https://github.com/kjldev/purview-telemetry-sourcegenerator/wiki/Contributing) for development setup, testing instructions, and the changeset-based release workflow. + +Every user-facing change requires a [changeset](docs/release-process.md) — run `just changeset` to create one. diff --git a/bun.lock b/bun.lock index 5eddf3d6..fdb7d7cd 100644 --- a/bun.lock +++ b/bun.lock @@ -5,397 +5,228 @@ "": { "name": "purview-telemetry-sourcegenerator", "devDependencies": { - "commit-and-tag-version": "^12.6.1", + "@changesets/changelog-github": "^0.5.0", + "@changesets/cli": "^2.27.12", }, }, }, "packages": { - "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + "@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="], - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + "@changesets/apply-release-plan": ["@changesets/apply-release-plan@7.1.1", "", { "dependencies": { "@changesets/config": "^3.1.4", "@changesets/get-version-range-type": "^0.4.0", "@changesets/git": "^3.0.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "detect-indent": "^6.0.0", "fs-extra": "^7.0.1", "lodash.startcase": "^4.4.0", "outdent": "^0.5.0", "prettier": "^2.7.1", "resolve-from": "^5.0.0", "semver": "^7.5.3" } }, "sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA=="], - "@hutson/parse-repository-url": ["@hutson/parse-repository-url@3.0.2", "", {}, "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q=="], + "@changesets/assemble-release-plan": ["@changesets/assemble-release-plan@6.0.10", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.4", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "semver": "^7.5.3" } }, "sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A=="], - "@types/minimist": ["@types/minimist@1.2.5", "", {}, "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag=="], + "@changesets/changelog-git": ["@changesets/changelog-git@0.2.1", "", { "dependencies": { "@changesets/types": "^6.1.0" } }, "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q=="], - "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], + "@changesets/changelog-github": ["@changesets/changelog-github@0.5.2", "", { "dependencies": { "@changesets/get-github-info": "^0.7.0", "@changesets/types": "^6.1.0", "dotenv": "^8.1.0" } }, "sha512-HeGeDl8HaIGj9fQHo/tv5XKQ2SNEi9+9yl1Bss1jttPqeiASRXhfi0A2wv8yFKCp07kR1gpOI5ge6+CWNm1jPw=="], - "JSONStream": ["JSONStream@1.3.5", "", { "dependencies": { "jsonparse": "^1.2.0", "through": ">=2.2.7 <3" }, "bin": "bin.js" }, "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="], + "@changesets/cli": ["@changesets/cli@2.31.0", "", { "dependencies": { "@changesets/apply-release-plan": "^7.1.1", "@changesets/assemble-release-plan": "^6.0.10", "@changesets/changelog-git": "^0.2.1", "@changesets/config": "^3.1.4", "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.4", "@changesets/get-release-plan": "^4.0.16", "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.7", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@changesets/write": "^0.4.0", "@inquirer/external-editor": "^1.0.2", "@manypkg/get-packages": "^1.1.3", "ansi-colors": "^4.1.3", "enquirer": "^2.4.1", "fs-extra": "^7.0.1", "mri": "^1.2.0", "package-manager-detector": "^0.2.0", "picocolors": "^1.1.0", "resolve-from": "^5.0.0", "semver": "^7.5.3", "spawndamnit": "^3.0.1", "term-size": "^2.1.0" }, "bin": { "changeset": "bin.js" } }, "sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg=="], - "add-stream": ["add-stream@1.0.0", "", {}, "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ=="], + "@changesets/config": ["@changesets/config@3.1.4", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/get-dependents-graph": "^2.1.4", "@changesets/logger": "^0.1.1", "@changesets/should-skip-package": "^0.1.2", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1", "micromatch": "^4.0.8" } }, "sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q=="], - "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], - - "array-ify": ["array-ify@1.0.0", "", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="], - - "arrify": ["arrify@1.0.1", "", {}, "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA=="], - - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], - - "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - - "camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="], - - "camelcase-keys": ["camelcase-keys@6.2.2", "", { "dependencies": { "camelcase": "^5.3.1", "map-obj": "^4.0.0", "quick-lru": "^4.0.1" } }, "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg=="], - - "chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], - - "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + "@changesets/errors": ["@changesets/errors@0.2.0", "", { "dependencies": { "extendable-error": "^0.1.5" } }, "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow=="], - "color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="], + "@changesets/get-dependents-graph": ["@changesets/get-dependents-graph@2.1.4", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "picocolors": "^1.1.0", "semver": "^7.5.3" } }, "sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg=="], - "color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], + "@changesets/get-github-info": ["@changesets/get-github-info@0.7.0", "", { "dependencies": { "dataloader": "^1.4.0", "node-fetch": "^2.5.0" } }, "sha512-+i67Bmhfj9V4KfDeS1+Tz3iF32btKZB2AAx+cYMqDSRFP7r3/ZdGbjCo+c6qkyViN9ygDuBjzageuPGJtKGe5A=="], - "commit-and-tag-version": ["commit-and-tag-version@12.6.1", "", { "dependencies": { "chalk": "^2.4.2", "conventional-changelog": "4.0.0", "conventional-changelog-config-spec": "2.1.0", "conventional-changelog-conventionalcommits": "6.1.0", "conventional-recommended-bump": "7.0.1", "detect-indent": "^6.1.0", "detect-newline": "^3.1.0", "dotgitignore": "^2.1.0", "fast-xml-parser": "^5.2.5", "figures": "^3.2.0", "find-up": "^5.0.0", "git-semver-tags": "^5.0.1", "semver": "^7.7.2", "yaml": "^2.6.0", "yargs": "^17.7.2" }, "bin": { "commit-and-tag-version": "bin/cli.js" } }, "sha512-QNwgDDrg44oFAiLwXChOGabeGlkuaEvD7lUbLYleWLmOVYqFidek0G6xUE1NbRtitkOrDx8fuFh8w17+nUCOYg=="], + "@changesets/get-release-plan": ["@changesets/get-release-plan@4.0.16", "", { "dependencies": { "@changesets/assemble-release-plan": "^6.0.10", "@changesets/config": "^3.1.4", "@changesets/pre": "^2.0.2", "@changesets/read": "^0.6.7", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g=="], - "compare-func": ["compare-func@2.0.0", "", { "dependencies": { "array-ify": "^1.0.0", "dot-prop": "^5.1.0" } }, "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="], + "@changesets/get-version-range-type": ["@changesets/get-version-range-type@0.4.0", "", {}, "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ=="], - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + "@changesets/git": ["@changesets/git@3.0.4", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@manypkg/get-packages": "^1.1.3", "is-subdir": "^1.1.1", "micromatch": "^4.0.8", "spawndamnit": "^3.0.1" } }, "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw=="], - "concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], + "@changesets/logger": ["@changesets/logger@0.1.1", "", { "dependencies": { "picocolors": "^1.1.0" } }, "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg=="], - "conventional-changelog": ["conventional-changelog@4.0.0", "", { "dependencies": { "conventional-changelog-angular": "^6.0.0", "conventional-changelog-atom": "^3.0.0", "conventional-changelog-codemirror": "^3.0.0", "conventional-changelog-conventionalcommits": "^6.0.0", "conventional-changelog-core": "^5.0.0", "conventional-changelog-ember": "^3.0.0", "conventional-changelog-eslint": "^4.0.0", "conventional-changelog-express": "^3.0.0", "conventional-changelog-jquery": "^4.0.0", "conventional-changelog-jshint": "^3.0.0", "conventional-changelog-preset-loader": "^3.0.0" } }, "sha512-JbZjwE1PzxQCvm+HUTIr+pbSekS8qdOZzMakdFyPtdkEWwFvwEJYONzjgMm0txCb2yBcIcfKDmg8xtCKTdecNQ=="], + "@changesets/parse": ["@changesets/parse@0.4.3", "", { "dependencies": { "@changesets/types": "^6.1.0", "js-yaml": "^4.1.1" } }, "sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A=="], - "conventional-changelog-angular": ["conventional-changelog-angular@6.0.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg=="], + "@changesets/pre": ["@changesets/pre@2.0.2", "", { "dependencies": { "@changesets/errors": "^0.2.0", "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3", "fs-extra": "^7.0.1" } }, "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug=="], - "conventional-changelog-atom": ["conventional-changelog-atom@3.0.0", "", {}, "sha512-pnN5bWpH+iTUWU3FaYdw5lJmfWeqSyrUkG+wyHBI9tC1dLNnHkbAOg1SzTQ7zBqiFrfo55h40VsGXWMdopwc5g=="], + "@changesets/read": ["@changesets/read@0.6.7", "", { "dependencies": { "@changesets/git": "^3.0.4", "@changesets/logger": "^0.1.1", "@changesets/parse": "^0.4.3", "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "p-filter": "^2.1.0", "picocolors": "^1.1.0" } }, "sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA=="], - "conventional-changelog-codemirror": ["conventional-changelog-codemirror@3.0.0", "", {}, "sha512-wzchZt9HEaAZrenZAUUHMCFcuYzGoZ1wG/kTRMICxsnW5AXohYMRxnyecP9ob42Gvn5TilhC0q66AtTPRSNMfw=="], + "@changesets/should-skip-package": ["@changesets/should-skip-package@0.1.2", "", { "dependencies": { "@changesets/types": "^6.1.0", "@manypkg/get-packages": "^1.1.3" } }, "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw=="], - "conventional-changelog-config-spec": ["conventional-changelog-config-spec@2.1.0", "", {}, "sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ=="], + "@changesets/types": ["@changesets/types@6.1.0", "", {}, "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA=="], - "conventional-changelog-conventionalcommits": ["conventional-changelog-conventionalcommits@6.1.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw=="], + "@changesets/write": ["@changesets/write@0.4.0", "", { "dependencies": { "@changesets/types": "^6.1.0", "fs-extra": "^7.0.1", "human-id": "^4.1.1", "prettier": "^2.7.1" } }, "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q=="], - "conventional-changelog-core": ["conventional-changelog-core@5.0.2", "", { "dependencies": { "add-stream": "^1.0.0", "conventional-changelog-writer": "^6.0.0", "conventional-commits-parser": "^4.0.0", "dateformat": "^3.0.3", "get-pkg-repo": "^4.2.1", "git-raw-commits": "^3.0.0", "git-remote-origin-url": "^2.0.0", "git-semver-tags": "^5.0.0", "normalize-package-data": "^3.0.3", "read-pkg": "^3.0.0", "read-pkg-up": "^3.0.0" } }, "sha512-RhQOcDweXNWvlRwUDCpaqXzbZemKPKncCWZG50Alth72WITVd6nhVk9MJ6w1k9PFNBcZ3YwkdkChE+8+ZwtUug=="], + "@inquirer/external-editor": ["@inquirer/external-editor@1.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA=="], - "conventional-changelog-ember": ["conventional-changelog-ember@3.0.0", "", {}, "sha512-7PYthCoSxIS98vWhVcSphMYM322OxptpKAuHYdVspryI0ooLDehRXWeRWgN+zWSBXKl/pwdgAg8IpLNSM1/61A=="], + "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], - "conventional-changelog-eslint": ["conventional-changelog-eslint@4.0.0", "", {}, "sha512-nEZ9byP89hIU0dMx37JXQkE1IpMmqKtsaR24X7aM3L6Yy/uAtbb+ogqthuNYJkeO1HyvK7JsX84z8649hvp43Q=="], + "@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="], - "conventional-changelog-express": ["conventional-changelog-express@3.0.0", "", {}, "sha512-HqxihpUMfIuxvlPvC6HltA4ZktQEUan/v3XQ77+/zbu8No/fqK3rxSZaYeHYant7zRxQNIIli7S+qLS9tX9zQA=="], + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], - "conventional-changelog-jquery": ["conventional-changelog-jquery@4.0.0", "", {}, "sha512-TTIN5CyzRMf8PUwyy4IOLmLV2DFmPtasKN+x7EQKzwSX8086XYwo+NeaeA3VUT8bvKaIy5z/JoWUvi7huUOgaw=="], + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], - "conventional-changelog-jshint": ["conventional-changelog-jshint@3.0.0", "", { "dependencies": { "compare-func": "^2.0.0" } }, "sha512-bQof4byF4q+n+dwFRkJ/jGf9dCNUv4/kCDcjeCizBvfF81TeimPZBB6fT4HYbXgxxfxWXNl/i+J6T0nI4by6DA=="], + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "conventional-changelog-preset-loader": ["conventional-changelog-preset-loader@3.0.0", "", {}, "sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA=="], + "@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], - "conventional-changelog-writer": ["conventional-changelog-writer@6.0.1", "", { "dependencies": { "conventional-commits-filter": "^3.0.0", "dateformat": "^3.0.3", "handlebars": "^4.7.7", "json-stringify-safe": "^5.0.1", "meow": "^8.1.2", "semver": "^7.0.0", "split": "^1.0.1" }, "bin": { "conventional-changelog-writer": "cli.js" } }, "sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ=="], + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], - "conventional-commits-filter": ["conventional-commits-filter@3.0.0", "", { "dependencies": { "lodash.ismatch": "^4.4.0", "modify-values": "^1.0.1" } }, "sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "conventional-commits-parser": ["conventional-commits-parser@4.0.0", "", { "dependencies": { "JSONStream": "^1.3.5", "is-text-path": "^1.0.1", "meow": "^8.1.2", "split2": "^3.2.2" }, "bin": { "conventional-commits-parser": "cli.js" } }, "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "conventional-recommended-bump": ["conventional-recommended-bump@7.0.1", "", { "dependencies": { "concat-stream": "^2.0.0", "conventional-changelog-preset-loader": "^3.0.0", "conventional-commits-filter": "^3.0.0", "conventional-commits-parser": "^4.0.0", "git-raw-commits": "^3.0.0", "git-semver-tags": "^5.0.0", "meow": "^8.1.2" }, "bin": { "conventional-recommended-bump": "cli.js" } }, "sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA=="], + "array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="], - "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + "better-path-resolve": ["better-path-resolve@1.0.0", "", { "dependencies": { "is-windows": "^1.0.0" } }, "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g=="], - "dargs": ["dargs@7.0.0", "", {}, "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg=="], + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "dateformat": ["dateformat@3.0.3", "", {}, "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="], + "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], - "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "decamelize-keys": ["decamelize-keys@1.1.1", "", { "dependencies": { "decamelize": "^1.1.0", "map-obj": "^1.0.0" } }, "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg=="], + "dataloader": ["dataloader@1.4.0", "", {}, "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw=="], "detect-indent": ["detect-indent@6.1.0", "", {}, "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA=="], - "detect-newline": ["detect-newline@3.1.0", "", {}, "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="], - - "dot-prop": ["dot-prop@5.3.0", "", { "dependencies": { "is-obj": "^2.0.0" } }, "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="], - - "dotgitignore": ["dotgitignore@2.1.0", "", { "dependencies": { "find-up": "^3.0.0", "minimatch": "^3.0.4" } }, "sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA=="], - - "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], - - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="], - "escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + "dotenv": ["dotenv@8.6.0", "", {}, "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g=="], - "fast-xml-parser": ["fast-xml-parser@5.3.7", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-JzVLro9NQv92pOM/jTCR6mHlJh2FGwtomH8ZQjhFj/R29P2Fnj38OgPJVtcvYw6SuKClhgYuwUZf5b3rd8u2mA=="], + "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], - "figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="], - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], - "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], - "get-pkg-repo": ["get-pkg-repo@4.2.1", "", { "dependencies": { "@hutson/parse-repository-url": "^3.0.0", "hosted-git-info": "^4.0.0", "through2": "^2.0.0", "yargs": "^16.2.0" }, "bin": { "get-pkg-repo": "src/cli.js" } }, "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "git-raw-commits": ["git-raw-commits@3.0.0", "", { "dependencies": { "dargs": "^7.0.0", "meow": "^8.1.2", "split2": "^3.2.2" }, "bin": { "git-raw-commits": "cli.js" } }, "sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw=="], + "find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - "git-remote-origin-url": ["git-remote-origin-url@2.0.0", "", { "dependencies": { "gitconfiglocal": "^1.0.0", "pify": "^2.3.0" } }, "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw=="], + "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], - "git-semver-tags": ["git-semver-tags@5.0.1", "", { "dependencies": { "meow": "^8.1.2", "semver": "^7.0.0" }, "bin": { "git-semver-tags": "cli.js" } }, "sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA=="], + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - "gitconfiglocal": ["gitconfiglocal@1.0.0", "", { "dependencies": { "ini": "^1.3.2" } }, "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ=="], + "globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - "handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="], - - "hard-rejection": ["hard-rejection@2.1.0", "", {}, "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA=="], - - "has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], - - "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + "human-id": ["human-id@4.1.3", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q=="], - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], - "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], + "is-subdir": ["is-subdir@1.2.0", "", { "dependencies": { "better-path-resolve": "1.0.0" } }, "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw=="], - "is-plain-obj": ["is-plain-obj@1.1.0", "", {}, "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="], + "is-windows": ["is-windows@1.0.2", "", {}, "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="], - "is-text-path": ["is-text-path@1.0.1", "", { "dependencies": { "text-extensions": "^1.0.0" } }, "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], - "json-parse-better-errors": ["json-parse-better-errors@1.0.2", "", {}, "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="], + "locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + "lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="], - "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], - "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - "load-json-file": ["load-json-file@4.0.0", "", { "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", "pify": "^3.0.0", "strip-bom": "^3.0.0" } }, "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="], + "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + "p-filter": ["p-filter@2.1.0", "", { "dependencies": { "p-map": "^2.0.0" } }, "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw=="], - "lodash.ismatch": ["lodash.ismatch@4.4.0", "", {}, "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g=="], + "p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], - "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + "p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - "map-obj": ["map-obj@4.3.0", "", {}, "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ=="], - - "meow": ["meow@8.1.2", "", { "dependencies": { "@types/minimist": "^1.2.0", "camelcase-keys": "^6.2.2", "decamelize-keys": "^1.1.0", "hard-rejection": "^2.1.0", "minimist-options": "4.1.0", "normalize-package-data": "^3.0.0", "read-pkg-up": "^7.0.1", "redent": "^3.0.0", "trim-newlines": "^3.0.0", "type-fest": "^0.18.0", "yargs-parser": "^20.2.3" } }, "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q=="], - - "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], - - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], - - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "minimist-options": ["minimist-options@4.1.0", "", { "dependencies": { "arrify": "^1.0.1", "is-plain-obj": "^1.1.0", "kind-of": "^6.0.3" } }, "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A=="], - - "modify-values": ["modify-values@1.0.1", "", {}, "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="], - - "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], - - "normalize-package-data": ["normalize-package-data@3.0.3", "", { "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" } }, "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA=="], - - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + "p-map": ["p-map@2.1.0", "", {}, "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="], "p-try": ["p-try@2.2.0", "", {}, "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="], - "parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="], + "package-manager-detector": ["package-manager-detector@0.2.11", "", { "dependencies": { "quansync": "^0.2.7" } }, "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ=="], "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], - "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "path-type": ["path-type@3.0.0", "", { "dependencies": { "pify": "^3.0.0" } }, "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg=="], + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], + "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], - "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + "pify": ["pify@4.0.1", "", {}, "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g=="], - "quick-lru": ["quick-lru@4.0.1", "", {}, "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="], + "prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], - "read-pkg": ["read-pkg@3.0.0", "", { "dependencies": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", "path-type": "^3.0.0" } }, "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA=="], + "quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="], - "read-pkg-up": ["read-pkg-up@3.0.0", "", { "dependencies": { "find-up": "^2.0.0", "read-pkg": "^3.0.0" } }, "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "read-yaml-file": ["read-yaml-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.5", "js-yaml": "^3.6.1", "pify": "^4.0.1", "strip-bom": "^3.0.0" } }, "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA=="], - "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], - "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - - "spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "spdx-license-ids": ["spdx-license-ids@3.0.21", "", {}, "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg=="], + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "split": ["split@1.0.1", "", { "dependencies": { "through": "2" } }, "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg=="], + "spawndamnit": ["spawndamnit@3.0.1", "", { "dependencies": { "cross-spawn": "^7.0.5", "signal-exit": "^4.0.1" } }, "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg=="], - "split2": ["split2@3.2.2", "", { "dependencies": { "readable-stream": "^3.0.0" } }, "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg=="], - - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], - "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], - - "strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], - - "supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], - - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - - "text-extensions": ["text-extensions@1.9.0", "", {}, "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="], - - "through": ["through@2.3.8", "", {}, "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="], - - "through2": ["through2@2.0.5", "", { "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ=="], - - "trim-newlines": ["trim-newlines@3.0.1", "", {}, "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw=="], - - "type-fest": ["type-fest@0.18.1", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="], - - "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], - - "uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="], - - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - - "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], - - "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], - - "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - - "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], - - "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - - "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - - "yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="], - - "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], - - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - - "conventional-changelog-writer/semver": ["semver@7.6.3", "", { "bin": "bin/semver.js" }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="], - - "decamelize-keys/map-obj": ["map-obj@1.0.1", "", {}, "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="], - - "dotgitignore/find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], - - "get-pkg-repo/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], - - "git-semver-tags/semver": ["semver@7.6.3", "", { "bin": "bin/semver.js" }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="], - - "load-json-file/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], - - "meow/read-pkg-up": ["read-pkg-up@7.0.1", "", { "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="], - - "meow/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], - - "normalize-package-data/semver": ["semver@7.6.3", "", { "bin": "bin/semver.js" }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="], - - "path-type/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="], - - "read-pkg/normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="], - - "read-pkg-up/find-up": ["find-up@2.1.0", "", { "dependencies": { "locate-path": "^2.0.0" } }, "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ=="], - - "through2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - - "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "dotgitignore/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], - - "get-pkg-repo/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], - - "get-pkg-repo/yargs/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], - - "meow/read-pkg-up/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], - - "meow/read-pkg-up/read-pkg": ["read-pkg@5.2.0", "", { "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", "parse-json": "^5.0.0", "type-fest": "^0.6.0" } }, "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg=="], - - "meow/read-pkg-up/type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="], - - "read-pkg-up/find-up/locate-path": ["locate-path@2.0.0", "", { "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" } }, "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA=="], - - "read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], - - "read-pkg/normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - - "through2/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], - - "through2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - - "wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "dotgitignore/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], - - "dotgitignore/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], - - "meow/read-pkg-up/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - - "meow/read-pkg-up/read-pkg/normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="], - - "meow/read-pkg-up/read-pkg/parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="], - "meow/read-pkg-up/read-pkg/type-fest": ["type-fest@0.6.0", "", {}, "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - "read-pkg-up/find-up/locate-path/p-locate": ["p-locate@2.0.0", "", { "dependencies": { "p-limit": "^1.1.0" } }, "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg=="], + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], - "read-pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], + "universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - "dotgitignore/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "meow/read-pkg-up/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "meow/read-pkg-up/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], + "@manypkg/find-root/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], - "meow/read-pkg-up/read-pkg/normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + "@manypkg/get-packages/@changesets/types": ["@changesets/types@4.1.0", "", {}, "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw=="], - "read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@1.3.0", "", { "dependencies": { "p-try": "^1.0.0" } }, "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q=="], + "@manypkg/get-packages/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], - "meow/read-pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + "read-yaml-file/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], - "read-pkg-up/find-up/locate-path/p-locate/p-limit/p-try": ["p-try@1.0.0", "", {}, "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww=="], + "read-yaml-file/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], } } diff --git a/docs/release-process.md b/docs/release-process.md new file mode 100644 index 00000000..30817299 --- /dev/null +++ b/docs/release-process.md @@ -0,0 +1,891 @@ +# Release Process + +This document describes the complete release process for this repository. +It is **designed to be reusable** — copy it (along with the scripts in `scripts/`) to any other repository and substitute the org/repo names. + +--- + +## Table of Contents + +1. [Overview](#overview) +2. [Developer Workflow](#developer-workflow) +3. [Quick Setup (DSC Scripts)](#quick-setup-dsc-scripts) +4. [Manual Setup Guide](#manual-setup-guide) + - [Step 1 — Authenticate the release bot](#step-1--authenticate-the-release-bot) + - [Step 2 — Branch ruleset](#step-2--branch-ruleset-for-main) + - [Step 3 — Tag ruleset](#step-3--tag-ruleset-for-v) + - [Step 4 — Immutable releases](#step-4--immutable-releases) + - [Step 5 — Verify](#step-5--verify) +5. [Token Reference by Scope](#token-reference-by-scope) +6. [DSC Script Reference](#dsc-script-reference) +7. [Adapting to Another Repository](#adapting-to-another-repository) +8. [Troubleshooting](#troubleshooting) + +--- + +## Overview + +Releases are **fully automated and gated**. No human ever pushes a version tag or manually creates a GitHub release. The flow is: + +``` +Feature branch + → just changeset (describe your change) + → PR to main (changeset-check + ci-gate must pass) + → merge (release bot creates Version PR) + → Version PR (ci-gate must pass) + → merge (CD pipeline publishes the release) +``` + +### Why this is bullet-proof + +| Risk | Mitigation | +|------|------------| +| Tag pushed manually before CD runs | Tag ruleset: only `github-actions[bot]` or your GitHub App can create `v*` tags | +| Release created without build artifacts | Draft-first: NuGet assets attached before `--draft=false` | +| Published release tampered with | Immutable releases: published releases are locked | +| Mid-release failure leaves broken state | ERR trap: deletes draft + tag on any error → clean retry | +| Double-release on re-run | CD checks tag existence first; skips if already released | +| PR merged without a changeset | `changeset-check` required status check blocks merging | +| Version PR bypasses CI | `CHANGESET_TOKEN` / GitHub App ensures CI triggers on bot-created PR | + +### Workflow components + +| Component | File | Purpose | +|-----------|------|---------| +| Changeset bot | `.github/workflows/release-pr.yml` | Creates/updates Version PR on push to main | +| CD pipeline | `.github/workflows/cd.yml` | Triggers on `package.json` change; builds, tests, packs, releases | +| Changeset check | `.github/workflows/ci.yml` (`changeset-check` job) | Blocks PRs without a changeset file | +| CI gate | `.github/workflows/ci.yml` (`ci-gate` job) | Single required status check | +| Changeset config | `.changeset/config.json` | Tells the bot which repo and changelog format to use | +| Version script | `package.json` `version-packages` | Runs `changeset version` then `update-version.ts` | + +--- + +## Developer Workflow + +### 1. Make your changes + +```bash +git checkout -b feat/my-change +# ... make changes ... +``` + +### 2. Add a changeset + +```bash +just changeset +``` + +This opens an interactive prompt. Select the bump type: + +| Type | When to use | +|------|-------------| +| `patch` | Bug fixes, internal refactors, dependency bumps | +| `minor` | New features, new options, backwards-compatible additions | +| `major` | Breaking changes — any change that requires consumer code changes | + +A `.changeset/.md` file is created. Commit it with your changes. + +### 3. Open a PR + +```bash +git add . +git commit -m "feat: my change" +git push origin feat/my-change +# Open a PR to main +``` + +The `Changeset Check` status check will verify a `.changeset/*.md` file is present. + +If your PR needs no release note (docs-only, CI-only), add the `skip-changeset` label. + +### 4. Merge the PR + +Once `CI Gate` and `Changeset Check` are green, merge the PR. + +### 5. Version PR is created automatically + +The `release-pr.yml` workflow creates (or updates) a **Version Packages** PR: +- Branch: `changeset-release/main` +- Bumps `package.json` version, updates `CHANGELOG.md`, deletes consumed `.changeset/*.md` files, syncs docs/wiki + +Review, ensure CI passes, then merge it. + +### 6. Release is published automatically + +Merging the Version PR triggers `cd.yml`, which: +1. Reads the new version from `package.json` +2. Skips if the release tag already exists (idempotent) +3. Runs format check, build, tests (main + sample solution) +4. Packs NuGet artifacts +5. Creates a **draft** GitHub Release with assets attached +6. Publishes the draft → becomes immutable +7. On any failure: ERR trap deletes the draft + tag → clean retry + +### Checking pending changesets + +```bash +just changeset-status +``` + +--- + +## Quick Setup (DSC Scripts) + +Two idempotent scripts automate the repository configuration. Safe to re-run. + +### Bash (Linux / macOS / GitHub Actions) + +```bash +# Personal repo, fine-grained PAT +./scripts/setup-release.sh \ + --owner myuser --repo myrepo \ + --scope personal --token-type fine-grained + +# Organisation repo, GitHub App (fully automated) +./scripts/setup-release.sh \ + --owner myorg --repo myrepo \ + --scope org --token-type app \ + --app-id 123456 --app-pem ./my-app.private-key.pem + +# GitHub Enterprise Server, classic PAT +./scripts/setup-release.sh \ + --owner myorg --repo myrepo \ + --scope ghes --token-type classic \ + --ghes-host github.example.com + +# Dry run — see what would happen without making changes +./scripts/setup-release.sh --owner myorg --repo myrepo --dry-run +``` + +### PowerShell (Windows / cross-platform) + +```powershell +# Personal repo, fine-grained PAT +.\scripts\setup-release.ps1 ` + -Owner myuser -Repo myrepo ` + -Scope personal -TokenType fine-grained + +# Organisation repo, GitHub App (fully automated) +.\scripts\setup-release.ps1 ` + -Owner myorg -Repo myrepo ` + -Scope org -TokenType app ` + -AppId 123456 -AppPem .\my-app.private-key.pem + +# GitHub Enterprise Server, classic PAT +.\scripts\setup-release.ps1 ` + -Owner myorg -Repo myrepo ` + -Scope ghes -TokenType classic ` + -GhesHost github.example.com + +# Dry run +.\scripts\setup-release.ps1 -Owner myorg -Repo myrepo -DryRun +``` + +### What the scripts configure + +| Step | Automatable | Notes | +|------|-------------|-------| +| Branch ruleset (`main`) | ✅ Fully automated | Uses `gh api` | +| Tag ruleset (`v*`) | ✅ With GitHub App | App installation ID used as bypass actor | +| Tag ruleset (`v*`) | ⚠ UI step with PAT | `github-actions[bot]` bypass requires UI | +| `skip-changeset` label | ✅ Fully automated | Used by `changeset-check` job | +| `CHANGESET_TOKEN` secret | Guided | Script prints exact URL + instructions | +| `APP_ID` / `APP_PRIVATE_KEY` secrets | ✅ If `--app-id` and `--app-pem` provided | | +| Immutable releases | ⚠ UI step | No API endpoint exists | + +--- + +## Manual Setup Guide + +### Step 1 — Authenticate the release bot + +The Version PR bot uses `GITHUB_TOKEN` by default. But GitHub's anti-loop protection means `GITHUB_TOKEN`-created PRs **do not trigger CI** — so `CI Gate` never passes and the Version PR can never merge. + +You must configure one of the following three options: + +--- + +#### Option A — GitHub App (recommended for orgs and enterprises) + +GitHub Apps issue short-lived tokens per workflow run. The private key never expires; no rotation needed. + +**When to use:** +- Organisation or enterprise repos +- Any environment where token expiry management is undesirable +- When you also want the tag ruleset bypass to be automatable + +**Setup:** + +1. **Create the app** + + | Scope | URL | + |-------|-----| + | Personal | https://github.com/settings/apps/new | + | Organisation | `https://github.com/organizations/{org}/settings/apps/new` | + | GHEC | Same as organisation on github.com | + | GHES | `https://{hostname}/settings/apps/new` | + +2. **Fill in fields:** + - App name: `{org}-release-bot` (or any unique name) + - Homepage URL: your repository URL + - Webhook: **uncheck "Active"** + +3. **Set permissions:** + + | Permission | Level | Why | + |------------|-------|-----| + | Contents | Read & write | Push version-bump commits, CHANGELOG | + | Pull requests | Read & write | Create and update Version PR | + | Workflows | Read & write | Update workflow files if changeset touches `.github/` | + | Metadata | Read | Always required (auto-set) | + +4. **Click Create GitHub App** + +5. **Download private key:** + - On the app settings page → scroll to **Private keys** → **Generate a private key** + - Save the downloaded `.pem` file securely + +6. **Note the App ID** shown on the settings page + +7. **Install the app on the repository:** + - App settings → **Install App** → select your org/account → **Only select repositories** → pick the repo + +8. **Set secrets:** + + ```bash + gh secret set APP_ID --repo {owner}/{repo} --body "123456" + gh secret set APP_PRIVATE_KEY --repo {owner}/{repo} < /path/to/app.private-key.pem + ``` + + PowerShell: + ```powershell + gh secret set APP_ID --repo {owner}/{repo} --body "123456" + Get-Content .\app.private-key.pem -Raw | gh secret set APP_PRIVATE_KEY --repo {owner}/{repo} + ``` + +9. **Get installation ID** (needed for the tag ruleset bypass — see Step 3): + + ```bash + # Via gh CLI (once the app is installed on the repo): + gh api repos/{owner}/{repo}/installation --jq '.id' + + # Alternatively: App settings → Installations → click the installed org/account + # The installation ID appears in the URL: /installations/{id} + ``` + +**The `release-pr.yml` workflow auto-detects `APP_ID` and uses the App token when present.** + +--- + +#### Option B — Fine-grained Personal Access Token + +Scoped to a specific repository with exact permissions. Maximum expiry: 1 year. **Set a calendar reminder to rotate before expiry.** + +**When to use:** +- Personal repos or small teams +- GHES 3.10+ +- When a GitHub App is not practical + +**Required permissions:** + +| Permission | Level | Why | +|------------|-------|-----| +| Contents | Read & write | Push version-bump commits | +| Pull requests | Read & write | Create/update Version PR | +| Workflows | Read & write | Update `.github/workflows/` if touched | +| Metadata | Read | Auto-required | + +**Setup by scope:** + +
+Personal account + +1. Navigate to: https://github.com/settings/personal-access-tokens/new +2. **Resource owner:** your personal account +3. **Repository access:** Only select repositories → pick the repo +4. Set permissions (table above) and an expiry +5. Copy the token and set the secret: + ```bash + gh secret set CHANGESET_TOKEN --repo {owner}/{repo} + # paste when prompted + ``` + +
+ +
+Organisation-owned repository + +1. Ensure the org allows fine-grained PATs: + - Navigate to: `https://github.com/organizations/{org}/settings/personal-access-tokens` + - Set policy to **Allow fine-grained personal access tokens** + - If approval is required, submit a request and wait for admin approval + +2. Navigate to: https://github.com/settings/personal-access-tokens/new +3. **Resource owner:** select the **organisation** (not your personal account) +4. **Repository access:** Only select repositories → pick the repo +5. Set permissions (table above) and an expiry +6. **SAML SSO:** Fine-grained PATs do **not** require a separate SSO authorization step +7. Copy the token: + ```bash + gh secret set CHANGESET_TOKEN --repo {owner}/{repo} + ``` + +
+ +
+GitHub Enterprise Cloud (GHEC) + +Same as organisation above. Additionally: +- Enterprise admins can enforce a fine-grained PAT policy at the enterprise level +- Check with your enterprise admin if you get "policy does not allow" errors +- Enterprise-managed users (EMU): token creation may be restricted to the IdP — follow your enterprise's provisioning process + +
+ +
+GitHub Enterprise Server (GHES 3.10+) + +1. Ensure the org allows fine-grained PATs: + - Navigate to: `https://{hostname}/organizations/{org}/settings/personal-access-tokens` + +2. Navigate to: `https://{hostname}/settings/personal-access-tokens/new` +3. Set permissions (table above) and an expiry +4. Copy the token: + ```bash + GH_HOST={hostname} gh secret set CHANGESET_TOKEN --repo {owner}/{repo} + ``` + +> ⚠ **GHES < 3.10:** Fine-grained PATs are not available. Use Option C. + +
+ +--- + +#### Option C — Classic Personal Access Token (legacy / fallback) + +Grants broad `repo` scope across **all** repositories the token owner has access to. Use only when Options A and B are unavailable. + +**When to use:** +- GHES < 3.10 +- Organisation policy explicitly blocks fine-grained PATs and GitHub Apps + +**Setup by scope:** + +
+Personal account + +1. Navigate to: https://github.com/settings/tokens/new +2. Scope: `repo` +3. Set an expiry (recommended, even though it's optional) +4. Copy the token: + ```bash + gh secret set CHANGESET_TOKEN --repo {owner}/{repo} + ``` + +
+ +
+Organisation-owned repository + +1. Navigate to: https://github.com/settings/tokens/new +2. Scope: `repo` +3. **SAML SSO:** After creating the token, click **"Authorize"** next to the organisation name +4. Copy the token: + ```bash + gh secret set CHANGESET_TOKEN --repo {owner}/{repo} + ``` + +
+ +
+GitHub Enterprise Cloud (GHEC) + +Same as organisation. SAML SSO authorization is required. If the enterprise uses IP allow lists, ensure the CI runner IPs are allowed. + +
+ +
+GitHub Enterprise Server (GHES any version) + +1. Navigate to: `https://{hostname}/settings/tokens/new` +2. Scope: `repo` +3. Copy the token: + ```bash + GH_HOST={hostname} gh secret set CHANGESET_TOKEN --repo {owner}/{repo} + ``` + +
+ +--- + +#### Option summary + +| Scenario | Recommended option | Token expiry | Tag ruleset bypass automatable | +|----------|-------------------|--------------|-------------------------------| +| Organisation or enterprise | **A — GitHub App** | Never (private key) | ✅ Yes | +| Personal repo, small team | **B — Fine-grained PAT** | Max 1 year | ⚠ UI step | +| GHES < 3.10 | **C — Classic PAT** | Optional | ⚠ UI step | +| Org blocks fine-grained PATs | **A or C** | — | ✅ A / ⚠ C | + +--- + +### Step 2 — Branch ruleset for `main` + +Requires a PR and `CI Gate` to pass before any commit reaches `main`. + +**Automated (already applied to this repo):** + +```bash +gh api repos/{owner}/{repo}/rulesets \ + --method POST \ + --header "Content-Type: application/json" \ + --input - <<'EOF' +{ + "name": "Protect main branch", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { "include": ["refs/heads/main"], "exclude": [] } + }, + "rules": [ + { "type": "deletion" }, + { "type": "non_fast_forward" }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 0, + "dismiss_stale_reviews_on_push": false, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_review_thread_resolution": false + } + }, + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": false, + "required_status_checks": [{ "context": "CI Gate" }] + } + } + ] +} +EOF +``` + +PowerShell equivalent: +```powershell +$body = @{ + name = "Protect main branch"; target = "branch"; enforcement = "active" + conditions = @{ ref_name = @{ include = @("refs/heads/main"); exclude = @() } } + rules = @( + @{ type = "deletion" } + @{ type = "non_fast_forward" } + @{ type = "pull_request" + parameters = @{ required_approving_review_count = 0 } } + @{ type = "required_status_checks" + parameters = @{ strict_required_status_checks_policy = $false + required_status_checks = @(@{ context = "CI Gate" }) } } + ) +} | ConvertTo-Json -Depth 10 + +$body | gh api repos/{owner}/{repo}/rulesets --method POST --header "Content-Type: application/json" --input - +``` + +**Via UI:** Settings → Rules → Rulesets → New ruleset → New branch ruleset + +| Setting | Value | +|---------|-------| +| Ruleset name | `Protect main branch` | +| Target branches | `refs/heads/main` | +| Rules | ✅ Restrict deletions | +| | ✅ Block force pushes | +| | ✅ Require a pull request before merging (0 approvals) | +| | ✅ Require status checks → add `CI Gate` | + +> **For GHES:** Rulesets require GHES 3.11+. On earlier versions use branch protection rules: +> ```bash +> GH_HOST={hostname} gh api repos/{owner}/{repo}/branches/main/protection \ +> --method PUT \ +> --input - <<'EOF' +> { +> "required_status_checks": { "strict": false, "contexts": ["CI Gate"] }, +> "enforce_admins": false, +> "required_pull_request_reviews": null, +> "restrictions": null +> } +> EOF +> ``` + +--- + +### Step 3 — Tag ruleset for `v*` + +Prevents anyone from pushing `v*` tags manually. Tags can only be created by the CD pipeline. + +> **This is the most important security guard in the release flow.** + +#### With GitHub App (Option A) — fully automatable + +```bash +# 1. Get the app's installation ID on the repo: +INSTALLATION_ID=$(gh api repos/{owner}/{repo}/installation --jq '.id') + +# 2. Create the tag ruleset with the App as bypass actor: +gh api repos/{owner}/{repo}/rulesets \ + --method POST \ + --header "Content-Type: application/json" \ + --input - < For **GHES**: Rulesets require GHES 3.11+. On earlier versions, the tag ruleset cannot be enforced via rules. Enforce via policy instead: document that tags must only be created via the CD pipeline. + +--- + +### Step 4 — Immutable releases + +Immutable releases prevent any modification to a published release — assets cannot be replaced or deleted, and the release body cannot be changed. + +**Why it's safe with this release flow:** + +- `gh release create --draft` creates a **draft** — drafts are never immutable +- Assets are uploaded to the draft (fully mutable at this point) +- `gh release edit --draft=false` publishes and locks the release +- The `trap cleanup ERR` fires on any error **before** the `--draft=false` line +- Therefore: failures always clean up a mutable draft; a locked release is never left broken + +**Enable via UI:** + +Settings → General → Releases → ✅ **Immutable releases** + +> There is no REST API endpoint for this setting. + +--- + +### Step 5 — Verify + +1. Create a feature branch, run `just changeset`, commit and open a PR +2. Verify `Changeset Check` and `CI Gate` appear as required status checks on the PR +3. Merge the PR; verify a `changeset-release/main` PR appears within minutes +4. Verify CI runs on the Version PR (`CI Gate` passes) +5. Merge the Version PR; verify the CD pipeline runs and publishes a GitHub Release with NuGet assets attached +6. Verify the release is marked as immutable (lock icon on the release page) +7. Attempt to push a `v*` tag manually — verify it is rejected by the tag ruleset + +--- + +## Token Reference by Scope + +Quick reference covering all combinations of scope and token type. + +| Scope | Token type | Where to create | Org admin action needed? | SAML SSO step? | Notes | +|-------|-----------|-----------------|--------------------------|---------------|-------| +| Personal | GitHub App | https://github.com/settings/apps/new | No | No | Preferred; no expiry | +| Personal | Fine-grained PAT | https://github.com/settings/personal-access-tokens/new | No | No | Max 1 year; set reminder | +| Personal | Classic PAT | https://github.com/settings/tokens/new | No | No | Broad scope; avoid if possible | +| Org | GitHub App | https://github.com/organizations/{org}/settings/apps/new | To install on org | No | Best for teams | +| Org | Fine-grained PAT | https://github.com/settings/personal-access-tokens/new | Yes — allow fine-grained PATs | No | Org approval may be required | +| Org | Classic PAT | https://github.com/settings/tokens/new | No (but SSO authz needed) | Yes — authorize for org | Broad scope; fallback only | +| GHEC | GitHub App | `https://github.com/organizations/{org}/settings/apps/new` | Yes — enterprise may restrict | No | EMU: check enterprise policy | +| GHEC | Fine-grained PAT | https://github.com/settings/personal-access-tokens/new | Yes — org and enterprise must allow | No | Enterprise policy may block | +| GHEC | Classic PAT | https://github.com/settings/tokens/new | No (SSO authz needed) | Yes | Broad scope | +| GHES 3.10+ | Fine-grained PAT | `https://{hostname}/settings/personal-access-tokens/new` | Yes — org must allow | Depends on GHES config | GHES 3.10+ only | +| GHES any | Classic PAT | `https://{hostname}/settings/tokens/new` | No | Depends on GHES config | Works on all GHES versions | +| GHES any | GitHub App | `https://{hostname}/settings/apps/new` | To install | No | Fully supported on all GHES | + +### Secret names and workflow detection + +The `release-pr.yml` workflow auto-detects which authentication method to use: + +```yaml +# Prefers GitHub App when APP_ID is present; falls back to CHANGESET_TOKEN +- uses: actions/create-github-app-token@v1 + id: app-token + if: ${{ secrets.APP_ID != '' }} + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + +# Token used for checkout and changeset action: +token: ${{ steps.app-token.outputs.token || secrets.CHANGESET_TOKEN }} +``` + +| Secret name | Used when | Option | +|-------------|-----------|--------| +| `APP_ID` + `APP_PRIVATE_KEY` | GitHub App token is preferred | A | +| `CHANGESET_TOKEN` | Fallback when `APP_ID` is not set | B or C | + +--- + +## DSC Script Reference + +Both scripts (`setup-release.sh` and `setup-release.ps1`) are idempotent DSC-style configuration scripts. Safe to re-run at any time. + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `--owner` / `-Owner` | string | ✅ | — | GitHub user or org name | +| `--repo` / `-Repo` | string | ✅ | — | Repository name | +| `--scope` / `-Scope` | enum | — | `personal` | `personal` · `org` · `enterprise` · `ghes` | +| `--token-type` / `-TokenType` | enum | — | `app` | `app` · `fine-grained` · `classic` | +| `--ghes-host` / `-GhesHost` | string | When scope=ghes | — | GHES hostname, e.g. `github.example.com` | +| `--app-id` / `-AppId` | string | — | — | GitHub App ID (used to set secret and get installation) | +| `--app-pem` / `-AppPem` | path | — | — | Path to `.pem` private key (used to set secret) | +| `--dry-run` / `-DryRun` | flag | — | false | Print actions without applying | + +### What each step does + +| Step | Action | Idempotency check | +|------|--------|-------------------| +| 1. Branch ruleset | `POST /repos/{r}/rulesets` | Checks for existing ruleset by name | +| 2. Tag ruleset | `POST /repos/{r}/rulesets` (with bypass if App) | Checks for existing ruleset by name | +| 3. `skip-changeset` label | `POST /repos/{r}/labels` | Checks for existing label by name | +| 4. Secrets | `gh secret set` | Checks `GET /repos/{r}/actions/secrets/{name}` | +| 5. Immutable releases | Prints UI instructions | Always prints (no API check possible) | + +### Examples for all scope × token-type combinations + +```bash +# personal × app +./scripts/setup-release.sh --owner myuser --repo myrepo \ + --scope personal --token-type app \ + --app-id 123456 --app-pem ./app.pem + +# personal × fine-grained +./scripts/setup-release.sh --owner myuser --repo myrepo \ + --scope personal --token-type fine-grained + +# personal × classic +./scripts/setup-release.sh --owner myuser --repo myrepo \ + --scope personal --token-type classic + +# org × app +./scripts/setup-release.sh --owner myorg --repo myrepo \ + --scope org --token-type app \ + --app-id 123456 --app-pem ./app.pem + +# org × fine-grained +./scripts/setup-release.sh --owner myorg --repo myrepo \ + --scope org --token-type fine-grained + +# enterprise (GHEC) × app +./scripts/setup-release.sh --owner myenterprise --repo myrepo \ + --scope enterprise --token-type app \ + --app-id 123456 --app-pem ./app.pem + +# ghes × classic (GHES < 3.10) +./scripts/setup-release.sh --owner myorg --repo myrepo \ + --scope ghes --token-type classic \ + --ghes-host github.example.com + +# ghes × fine-grained (GHES 3.10+) +./scripts/setup-release.sh --owner myorg --repo myrepo \ + --scope ghes --token-type fine-grained \ + --ghes-host github.example.com + +# dry run — inspect without changing anything +./scripts/setup-release.sh --owner myorg --repo myrepo \ + --scope org --token-type app --dry-run +``` + +--- + +## Adapting to Another Repository + +1. **Copy files:** + ``` + .changeset/config.json → update "repo" to "{owner}/{repo}" + .changeset/README.md + .github/workflows/release-pr.yml + .github/workflows/cd.yml → update MAIN_SOLUTION, SAMPLE_SOLUTION, PACKAGE_PROJECT + scripts/setup-release.sh + scripts/setup-release.ps1 + ``` + Plus add the `changeset-check` job and `ci-gate` updates to your existing CI workflow. + +2. **Install dependencies:** + ```bash + bun add -D @changesets/cli @changesets/changelog-github + ``` + +3. **Add scripts to `package.json`:** + ```json + { + "scripts": { + "changeset": "changeset", + "version-packages": "changeset version" + } + } + ``` + If you have a version-sync script (like `update-version.ts`), append `&& bun .build/update-version.ts`. + +4. **Add Justfile recipes:** + ```makefile + changeset: + bun changeset + + changeset-status: + bun changeset status + ``` + +5. **Configure the repository** (run the DSC script): + ```bash + ./scripts/setup-release.sh --owner {owner} --repo {repo} \ + --scope {personal|org|enterprise|ghes} --token-type {app|fine-grained|classic} + ``` + +6. **Add secrets** (see Step 1 above for your chosen token type) + +7. **Complete manual steps:** + - Tag ruleset bypass (if using PAT): GitHub UI → add "GitHub Actions" bypass actor + - Immutable releases: Settings → General → ✅ Immutable releases + +8. **Adapt `cd.yml`** for your project type. Key variables: + ```yaml + env: + MAIN_SOLUTION: ./src/MyProject.slnx + SAMPLE_SOLUTION: ./samples/SampleApp/SampleApp.slnx # remove if no samples + PACKAGE_PROJECT: ./src/MyProject/MyProject.csproj + ARTIFACTS_DIR: ./artifacts/nuget + ``` + For non-.NET projects, replace the build/test/pack steps with your equivalent commands. + +--- + +## Troubleshooting + +### Version PR not triggering CI + +The `CHANGESET_TOKEN` secret is missing, expired, or has insufficient permissions. The bot falls back to `GITHUB_TOKEN` which cannot trigger workflow runs. + +**Diagnose:** Check the `release-pr.yml` run logs for "Generate GitHub App token" step — if it was skipped (APP_ID not set) and CHANGESET_TOKEN is empty, the PR was created with GITHUB_TOKEN. + +**Fix:** +1. Set or rotate the `CHANGESET_TOKEN` secret (or `APP_ID` + `APP_PRIVATE_KEY`) +2. Close and reopen the Version PR, or trigger the workflow manually: + ```bash + gh workflow run release-pr.yml --repo {owner}/{repo} + ``` + +### CD pipeline skips the release + +The release tag already exists. This is intentional idempotency. + +```bash +# Check what's there: +gh release view v{version} --repo {owner}/{repo} + +# If the existing release is broken (no assets), delete it: +gh release delete v{version} --cleanup-tag --yes --repo {owner}/{repo} + +# Re-trigger the CD pipeline: +gh workflow run cd.yml --repo {owner}/{repo} +``` + +### CD pipeline fails mid-release + +The ERR trap should have cleaned up automatically. Verify: +```bash +# Check no draft release was left behind: +gh release list --repo {owner}/{repo} + +# Check no orphaned tag was left: +git ls-remote --tags https://github.com/{owner}/{repo} 'refs/tags/v*' +``` + +If a draft or tag was left, clean up manually: +```bash +gh release delete v{version} --cleanup-tag --yes --repo {owner}/{repo} +``` + +Then re-trigger: +```bash +gh workflow run cd.yml --repo {owner}/{repo} +``` + +### Tag push rejected by ruleset + +``` +remote: error: GH013: Repository rule violations found for refs/tags/v4.3.0 +``` + +This is the tag ruleset working correctly. Tags must be created via the CD pipeline (by merging a Version PR). Do not push tags manually. + +### Changeset check fails — no changeset file + +``` +Error: No changeset file found. +``` + +Run `just changeset` on your feature branch. If the PR is genuinely docs-only or CI-only, add the `skip-changeset` label. + +### CHANGESET_TOKEN expired + +The Version PR will be created with `GITHUB_TOKEN` (no CI runs) or the bot will fail entirely. + +**Fix:** Rotate the token, update the secret, then re-trigger `release-pr.yml`: +```bash +gh workflow run release-pr.yml --repo {owner}/{repo} +``` + +### Fine-grained PAT rejected — org approval required + +The org requires admin approval for fine-grained PATs. You'll see an error in the workflow logs mentioning "approval" or "policy". + +**Fix:** Contact an org admin to approve the PAT at: +`https://github.com/organizations/{org}/settings/personal-access-tokens` + +Or switch to Option A (GitHub App), which doesn't require per-token approval. + +### GHES: `gh api` returns wrong results + +Ensure `GH_HOST` is set: +```bash +export GH_HOST=github.example.com +gh api repos/{owner}/{repo}/rulesets +``` + +Or pass it inline: +```bash +GH_HOST=github.example.com gh api repos/{owner}/{repo}/rulesets +``` diff --git a/package.json b/package.json index 950ca675..8110f4bf 100644 --- a/package.json +++ b/package.json @@ -4,24 +4,21 @@ "description": "Generates [`ActivitySource`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.activitysource), [`ILogger`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger), and [`Metrics`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics) based on interface methods.", "readme": "README.md", "scripts": { - "release": "commit-and-tag-version --commit-all" + "changeset": "changeset", + "version-packages": "changeset version && bun .build/update-version.ts" }, "repository": { "type": "git", - "url": "git+https://github.com/purview-dev/purview-telemetry-sourcegenerator.git" + "url": "git+https://github.com/kjldev/purview-telemetry-sourcegenerator.git" }, "author": "Kieron Lanning (https://github.com/kieronlanning/)", "license": "ISC", "bugs": { - "url": "https://github.com/purview-dev/purview-telemetry-sourcegenerator/issues" + "url": "https://github.com/kjldev/purview-telemetry-sourcegenerator/issues" }, - "homepage": "https://github.com/purview-dev/purview-telemetry-sourcegenerator#readme", + "homepage": "https://github.com/kjldev/purview-telemetry-sourcegenerator#readme", "devDependencies": { - "commit-and-tag-version": "^12.6.1" - }, - "commit-and-tag-version": { - "scripts": { - "postbump": "just update-version && git add ." - } + "@changesets/changelog-github": "^0.5.0", + "@changesets/cli": "^2.27.12" } } diff --git a/scripts/setup-release.ps1 b/scripts/setup-release.ps1 new file mode 100644 index 00000000..db2f4afa --- /dev/null +++ b/scripts/setup-release.ps1 @@ -0,0 +1,436 @@ +<# +.SYNOPSIS + Desired-State Configuration for the changeset release flow. + +.DESCRIPTION + Idempotent. Safe to re-run: existing rulesets and labels are checked first. + Configures the repository to use the @changesets/cli release flow with + draft-first GitHub Releases that are compatible with immutable releases. + + Handles all account scopes (personal, org, enterprise, GHES) and all token + types (GitHub App, fine-grained PAT, classic PAT). + +.PARAMETER Owner + GitHub owner — user name or organisation name. Required. + +.PARAMETER Repo + Repository name. Required. + +.PARAMETER Scope + Account scope. One of: personal, org, enterprise, ghes. + Default: personal + +.PARAMETER TokenType + How the release bot authenticates. One of: app, fine-grained, classic. + Default: app + +.PARAMETER GhesHost + Hostname for GitHub Enterprise Server (e.g. github.example.com). + Required when -Scope ghes. + +.PARAMETER AppId + GitHub App ID. Used with -TokenType app to set the APP_ID secret + and retrieve the installation ID for the tag ruleset bypass. + +.PARAMETER AppPem + Path to the GitHub App private-key .pem file. + Used with -TokenType app to set APP_PRIVATE_KEY and generate a JWT + to retrieve the app installation ID. + +.PARAMETER DryRun + Print what would be done without making changes. + +.EXAMPLE + # Personal repo, fine-grained PAT + .\scripts\setup-release.ps1 -Owner myuser -Repo myrepo ` + -Scope personal -TokenType fine-grained + +.EXAMPLE + # Organisation repo, GitHub App (fully automated tag ruleset bypass) + .\scripts\setup-release.ps1 -Owner myorg -Repo myrepo ` + -Scope org -TokenType app ` + -AppId 123456 -AppPem .\my-app.private-key.pem + +.EXAMPLE + # GitHub Enterprise Server, classic PAT + .\scripts\setup-release.ps1 -Owner myorg -Repo myrepo ` + -Scope ghes -TokenType classic ` + -GhesHost github.example.com + +.EXAMPLE + # Dry run — see what would happen without making changes + .\scripts\setup-release.ps1 -Owner myorg -Repo myrepo -DryRun + +.NOTES + Requires the GitHub CLI (gh) to be installed and authenticated. + For GitHub App tag-ruleset bypass: the app must be installed on the + repository before running this script. +#> + +[CmdletBinding(SupportsShouldProcess)] +param ( + [Parameter(Mandatory)] + [string]$Owner, + + [Parameter(Mandatory)] + [string]$Repo, + + [ValidateSet('personal', 'org', 'enterprise', 'ghes')] + [string]$Scope = 'personal', + + [ValidateSet('app', 'fine-grained', 'classic')] + [string]$TokenType = 'app', + + [string]$GhesHost = '', + + [string]$AppId = '', + + [string]$AppPem = '', + + [switch]$DryRun +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +# ─── helpers ────────────────────────────────────────────────────────────────── +function Write-Step ([string]$msg) { Write-Host "`n▶ $msg" -ForegroundColor Cyan } +function Write-OK ([string]$msg) { Write-Host " ✅ $msg" -ForegroundColor Green } +function Write-Info ([string]$msg) { Write-Host " ℹ $msg" -ForegroundColor White } +function Write-Warn ([string]$msg) { Write-Host " ⚠ $msg" -ForegroundColor Yellow } +function Write-Dry ([string]$msg) { Write-Host " [dry-run] $msg" -ForegroundColor Yellow } +function Write-Err ([string]$msg) { Write-Host " ✖ $msg" -ForegroundColor Red; throw $msg } + +function Invoke-GhApi { + param([string]$Path, [string]$Method = 'GET', [string]$Body = '', [switch]$Silent) + $args = @("api", $Path) + if ($Method -ne 'GET') { $args += @('--method', $Method) } + if ($Body) { $args += @('--input', '-') } + if ($DryRun) { + Write-Dry "gh $($args -join ' ')$(if($Body){ ' ' })" + return $null + } + try { + if ($Body) { + $result = $Body | gh @args 2>&1 + } else { + $result = gh @args 2>&1 + } + if ($LASTEXITCODE -ne 0) { + if (-not $Silent) { throw "gh api $Path failed: $result" } + return $null + } + return $result | ConvertFrom-Json -ErrorAction SilentlyContinue + } catch { + if (-not $Silent) { throw } + return $null + } +} + +function Test-RulesetExists ([string]$Name) { + $rulesets = Invoke-GhApi "repos/$FullRepo/rulesets" -Silent + if ($null -eq $rulesets) { return $false } + return ($rulesets | Where-Object { $_.name -eq $Name }).Count -gt 0 +} + +function Test-LabelExists ([string]$Name) { + $labels = Invoke-GhApi "repos/$FullRepo/labels" -Silent + if ($null -eq $labels) { return $false } + return ($labels | Where-Object { $_.name -eq $Name }).Count -gt 0 +} + +function Test-SecretExists ([string]$Name) { + $secret = Invoke-GhApi "repos/$FullRepo/actions/secrets/$Name" -Silent + return $null -ne $secret +} + +function Show-TagRulesetUiInstructions { + Write-Host "" + Write-Host " Manual step required — Tag Ruleset:" -ForegroundColor White + Write-Host " 1. Navigate to: https://github.com/$FullRepo/settings/rules/new?target=tag" + Write-Host " 2. Ruleset name: Protect release tags" + Write-Host " 3. Target tags: refs/tags/v*" + Write-Host " 4. Rules: ✅ Restrict creations" + Write-Host " 5. Bypass actors: Add → search 'GitHub Actions' → select → Save" + Write-Host "" +} + +# ─── environment setup ──────────────────────────────────────────────────────── +if ($Scope -eq 'ghes') { + if (-not $GhesHost) { Write-Err "-GhesHost is required when -Scope ghes" } + $env:GH_HOST = $GhesHost + Write-Info "Targeting GHES host: $GhesHost" +} + +if ($Scope -eq 'ghes' -and $TokenType -eq 'fine-grained') { + # GHES < 3.10 does not support fine-grained PATs + Write-Warn "Fine-grained PATs require GHES 3.10+. If your server is older, use -TokenType classic." +} + +$FullRepo = "$Owner/$Repo" + +# ─── header ─────────────────────────────────────────────────────────────────── +Write-Host "" +Write-Host "Changeset Release Flow — Desired State Configuration" -ForegroundColor Cyan -NoNewline +if ($DryRun) { Write-Host " [DRY RUN]" -ForegroundColor Yellow } else { Write-Host "" } +Write-Host " Repo: $FullRepo" +Write-Host " Scope: $Scope" +Write-Host " Token type: $TokenType" +Write-Host "" + +# ─── Step 1: branch ruleset ─────────────────────────────────────────────────── +Write-Step "Step 1/4 — Branch ruleset (protect main)" + +$BranchRulesetName = 'Protect main branch' +if (Test-RulesetExists $BranchRulesetName) { + Write-OK "Branch ruleset '$BranchRulesetName' already exists — skipping." +} else { + Write-Info "Creating branch ruleset: $BranchRulesetName ..." + + $BranchRulesetBody = @{ + name = $BranchRulesetName + target = 'branch' + enforcement = 'active' + conditions = @{ + ref_name = @{ include = @('refs/heads/main'); exclude = @() } + } + rules = @( + @{ type = 'deletion' } + @{ type = 'non_fast_forward' } + @{ + type = 'pull_request' + parameters = @{ + required_approving_review_count = 0 + dismiss_stale_reviews_on_push = $false + require_code_owner_review = $false + require_last_push_approval = $false + required_review_thread_resolution = $false + } + } + @{ + type = 'required_status_checks' + parameters = @{ + strict_required_status_checks_policy = $false + required_status_checks = @( + @{ context = 'CI Gate' } + ) + } + } + ) + } | ConvertTo-Json -Depth 10 + + if (-not $DryRun) { + Invoke-GhApi "repos/$FullRepo/rulesets" -Method POST -Body $BranchRulesetBody | Out-Null + } else { + Write-Dry "POST repos/$FullRepo/rulesets (branch ruleset)" + } + Write-OK "Branch ruleset created." +} + +# ─── Step 2: tag ruleset ────────────────────────────────────────────────────── +Write-Step "Step 2/4 — Tag ruleset (protect v* tags)" + +$TagRulesetName = 'Protect release tags' +if (Test-RulesetExists $TagRulesetName) { + Write-OK "Tag ruleset '$TagRulesetName' already exists — skipping." +} else { + Write-Info "Configuring tag ruleset for v* ..." + + if ($TokenType -eq 'app' -and $AppId) { + # Use App ID directly as the Integration bypass actor. + # The actor_id for an Integration (GitHub App) bypass is the App's numeric ID, + # NOT the installation ID. The App must be installed on the repo/org first. + Write-Info "GitHub App detected — creating tag ruleset with App ID $AppId as bypass actor ..." + + $TagRulesetBody = @{ + name = $TagRulesetName + target = 'tag' + enforcement = 'active' + conditions = @{ + ref_name = @{ include = @('refs/tags/v*'); exclude = @() } + } + rules = @( + @{ type = 'creation' } + @{ type = 'deletion' } + @{ type = 'non_fast_forward' } + @{ type = 'update' } + ) + bypass_actors = @( + @{ + actor_id = [int]$AppId + actor_type = 'Integration' + bypass_mode = 'always' + } + ) + } | ConvertTo-Json -Depth 10 + + $created = $false + if (-not $DryRun) { + try { + Invoke-GhApi "repos/$FullRepo/rulesets" -Method POST -Body $TagRulesetBody | Out-Null + $created = $true + } catch { + Write-Warn "Tag ruleset API creation failed — ensure the App is installed on this repo/org." + } + } else { + Write-Dry "POST repos/$FullRepo/rulesets (tag ruleset, App bypass actor_id=$AppId)" + $created = $true + } + + if ($created) { + Write-OK "Tag ruleset created with GitHub App bypass (App ID $AppId)." + } else { + Show-TagRulesetUiInstructions + } + } else { + Write-Warn "Tag ruleset bypass for github-actions[bot] requires the GitHub UI." + Show-TagRulesetUiInstructions + } +} + +# ─── Step 3: skip-changeset label ───────────────────────────────────────────── +Write-Step "Step 3/4 — GitHub label: skip-changeset" + +if (Test-LabelExists 'skip-changeset') { + Write-OK "Label 'skip-changeset' already exists — skipping." +} else { + Write-Info "Creating label 'skip-changeset' ..." + if (-not $DryRun) { + $labelBody = @{ + name = 'skip-changeset' + color = 'e4e669' + description = 'Opt this PR out of the changeset requirement (docs-only, CI-only, etc.)' + } | ConvertTo-Json + Invoke-GhApi "repos/$FullRepo/labels" -Method POST -Body $labelBody | Out-Null + } else { + Write-Dry "POST repos/$FullRepo/labels { name: skip-changeset, color: e4e669 }" + } + Write-OK "Label 'skip-changeset' created." +} + +# ─── Step 4: secrets guidance ───────────────────────────────────────────────── +Write-Step "Step 4/4 — Repository secrets" + +switch ($TokenType) { + 'app' { + if (-not (Test-SecretExists 'APP_ID')) { + Write-Warn "Secret 'APP_ID' not set." + if ($AppId) { + if ($DryRun) { + Write-Dry "gh secret set APP_ID --repo $FullRepo --body $AppId" + } else { + gh secret set APP_ID --repo $FullRepo --body $AppId + Write-OK "APP_ID secret set." + } + } else { + Write-Host " Run: gh secret set APP_ID --repo $FullRepo" + } + } else { + Write-OK "Secret 'APP_ID' already set." + } + + if (-not (Test-SecretExists 'APP_PRIVATE_KEY')) { + Write-Warn "Secret 'APP_PRIVATE_KEY' not set." + if ($AppPem -and (Test-Path $AppPem)) { + if ($DryRun) { + Write-Dry "gh secret set APP_PRIVATE_KEY --repo $FullRepo < $AppPem" + } else { + Get-Content $AppPem -Raw | gh secret set APP_PRIVATE_KEY --repo $FullRepo + Write-OK "APP_PRIVATE_KEY secret set." + } + } else { + Write-Host " Run: Get-Content .\app.private-key.pem -Raw | gh secret set APP_PRIVATE_KEY --repo $FullRepo" + } + } else { + Write-OK "Secret 'APP_PRIVATE_KEY' already set." + } + } + + { $_ -in 'fine-grained', 'classic' } { + if (-not (Test-SecretExists 'CHANGESET_TOKEN')) { + Write-Warn "Secret 'CHANGESET_TOKEN' not set." + Write-Host "" + + switch ($TokenType) { + 'fine-grained' { + switch ($Scope) { + 'personal' { + Write-Host " Create a fine-grained PAT at:" + Write-Host " https://github.com/settings/personal-access-tokens/new" + Write-Host " Resource owner: $Owner" + Write-Host " Repository access: Only select → $Repo" + } + { $_ -in 'org', 'enterprise' } { + Write-Host " Ensure your org allows fine-grained PATs:" + Write-Host " https://github.com/organizations/$Owner/settings/personal-access-tokens" + Write-Host " Create a fine-grained PAT at:" + Write-Host " https://github.com/settings/personal-access-tokens/new" + Write-Host " Resource owner: $Owner (the org, not your personal account)" + Write-Host " Repository access: Only select → $Repo" + } + 'ghes' { + Write-Host " Requires GHES 3.10+. Create at:" + Write-Host " https://$GhesHost/settings/personal-access-tokens/new" + Write-Host " Ensure org allows fine-grained PATs:" + Write-Host " https://$GhesHost/organizations/$Owner/settings/personal-access-tokens" + } + } + Write-Host " Required permissions: Contents (r/w), Pull requests (r/w), Workflows (r/w)" + } + + 'classic' { + switch ($Scope) { + 'personal' { + Write-Host " Create a classic PAT at:" + Write-Host " https://github.com/settings/tokens/new" + } + { $_ -in 'org', 'enterprise' } { + Write-Host " Create a classic PAT at:" + Write-Host " https://github.com/settings/tokens/new" + Write-Host " Scope: repo" + Write-Host " SAML SSO: After creating, click 'Authorize' for org $Owner" + } + 'ghes' { + Write-Host " Create a classic PAT at:" + Write-Host " https://$GhesHost/settings/tokens/new" + } + } + Write-Host " Scope: repo (includes contents, pull requests, workflows)" + } + } + + Write-Host "" + Write-Host " Then run: gh secret set CHANGESET_TOKEN --repo $FullRepo" + } else { + Write-OK "Secret 'CHANGESET_TOKEN' already set." + } + } +} + +# ─── Step 5: immutable releases ─────────────────────────────────────────────── +Write-Host "" +Write-Host "Immutable releases (manual step)" -ForegroundColor Cyan +Write-Host "" +Write-Host " Enable at: https://github.com/$FullRepo/settings" +Write-Host " Under 'Releases' → check 'Immutable releases'." +Write-Host "" +Write-Host " The CD pipeline uses a draft-first approach so this is always safe:" +Write-Host " • Drafts are never immutable — assets attach freely" +Write-Host " • Publishing marks the release immutable (security guarantee)" +Write-Host " • The ERR cleanup trap fires before publish, so failures" +Write-Host " always clean up a mutable draft, never a locked release" +Write-Host "" + +# ─── Summary ────────────────────────────────────────────────────────────────── +Write-Host "" +Write-Host "════════════════════════════════════════" -ForegroundColor Green +Write-Host " Setup complete for $FullRepo" -ForegroundColor Green +Write-Host "════════════════════════════════════════" -ForegroundColor Green +Write-Host "" +Write-Host " Verify the setup:" +Write-Host " 1. Create a branch, run 'just changeset', commit and open a PR" +Write-Host " 2. Confirm 'Changeset Check' and 'CI Gate' appear as required checks" +Write-Host " 3. Merge the PR; verify a changeset-release/main PR appears" +Write-Host " 4. Verify CI runs on the Version PR" +Write-Host " 5. Merge the Version PR; verify a GitHub Release is published" +Write-Host "" diff --git a/scripts/setup-release.sh b/scripts/setup-release.sh new file mode 100644 index 00000000..61b1a5ba --- /dev/null +++ b/scripts/setup-release.sh @@ -0,0 +1,417 @@ +#!/usr/bin/env bash +# ============================================================================= +# setup-release.sh — Desired-State Configuration for the changeset release flow +# +# Idempotent. Safe to re-run: existing rulesets and labels are checked first. +# +# USAGE +# ./scripts/setup-release.sh [OPTIONS] +# +# OPTIONS +# --owner GitHub owner (user or org). Required. +# --repo Repository name. Required. +# --scope Account scope: personal | org | enterprise | ghes +# Default: personal +# --token-type How the release bot authenticates: app | fine-grained | classic +# Default: app +# --ghes-host Hostname for GHES. Required when --scope ghes. +# Example: github.example.com +# --app-id GitHub App ID (only needed with --token-type app) +# --app-pem Path to GitHub App private key .pem file +# (only needed with --token-type app, used for ruleset bypass) +# --dry-run Print what would be done without making changes. +# -h, --help Show this help message. +# +# EXAMPLES +# # Personal repo, fine-grained PAT +# ./scripts/setup-release.sh --owner myuser --repo myrepo \ +# --scope personal --token-type fine-grained +# +# # Organisation repo, GitHub App (fully automated tag ruleset bypass) +# ./scripts/setup-release.sh --owner myorg --repo myrepo \ +# --scope org --token-type app \ +# --app-id 123456 --app-pem ./my-app.private-key.pem +# +# # GitHub Enterprise Server, classic PAT +# ./scripts/setup-release.sh --owner myorg --repo myrepo \ +# --scope ghes --token-type classic \ +# --ghes-host github.example.com +# +# REQUIREMENTS +# - gh CLI authenticated (gh auth login) +# - For --token-type app + tag ruleset: the app must be installed on the repo +# before this script runs. +# ============================================================================= +set -euo pipefail + +# ─── defaults ──────────────────────────────────────────────────────────────── +OWNER="" +REPO="" +SCOPE="personal" +TOKEN_TYPE="app" +GHES_HOST="" +APP_ID="" +APP_PEM="" +DRY_RUN=false + +# ─── colours ───────────────────────────────────────────────────────────────── +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' +CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' +info() { echo -e "${CYAN} ℹ ${RESET}$*"; } +success() { echo -e "${GREEN} ✅ ${RESET}$*"; } +warning() { echo -e "${YELLOW} ⚠ ${RESET}$*"; } +error() { echo -e "${RED} ✖ ${RESET}$*" >&2; } +header() { echo -e "\n${BOLD}${CYAN}▶ $*${RESET}"; } +dry() { echo -e "${YELLOW} [dry-run]${RESET} $*"; } + +# ─── helper functions ───────────────────────────────────────────────────────── +gh_api() { + if [[ "${DRY_RUN}" == true ]]; then + dry "gh api $*" + return 0 + fi + gh api "$@" +} + +ruleset_exists() { + local name="$1" + gh api "repos/${FULL_REPO}/rulesets" --jq ".[] | select(.name == \"${name}\") | .id" 2>/dev/null | grep -q . +} + +label_exists() { + local name="$1" + gh api "repos/${FULL_REPO}/labels" --jq ".[].name" 2>/dev/null | grep -qxF "${name}" +} + +print_help() { + awk ' + /^# =============================================================================$/ { + delimiter_count++ + if (delimiter_count == 1) { + next + } + if (delimiter_count == 2) { + exit + } + } + delimiter_count >= 1 { + sub(/^# ?/, "") + print + } + ' "$0" +} + +_tag_ruleset_ui_instructions() { + echo "" + echo -e " ${BOLD}Manual step required — Tag Ruleset:${RESET}" + echo -e " 1. Navigate to: https://github.com/${FULL_REPO}/settings/rules/new?target=tag" + echo -e " 2. Name: Protect release tags" + echo -e " 3. Target: refs/tags/v*" + echo -e " 4. Rules: ✅ Restrict creations" + echo -e " 5. Bypass: Add → search 'GitHub Actions' → select it → Save" + echo "" +} + +# ─── argument parsing ───────────────────────────────────────────────────────── +while [[ $# -gt 0 ]]; do + case "$1" in + --owner) OWNER="$2"; shift 2 ;; + --repo) REPO="$2"; shift 2 ;; + --scope) SCOPE="$2"; shift 2 ;; + --token-type) TOKEN_TYPE="$2";shift 2 ;; + --ghes-host) GHES_HOST="$2"; shift 2 ;; + --app-id) APP_ID="$2"; shift 2 ;; + --app-pem) APP_PEM="$2"; shift 2 ;; + --dry-run) DRY_RUN=true; shift ;; + -h|--help) + print_help + exit 0 + ;; + *) error "Unknown argument: $1"; exit 1 ;; + esac +done + +# ─── validation ─────────────────────────────────────────────────────────────── +[[ -z "${OWNER}" ]] && { error "--owner is required"; exit 1; } +[[ -z "${REPO}" ]] && { error "--repo is required"; exit 1; } + +case "${SCOPE}" in personal|org|enterprise|ghes) ;; *) + error "--scope must be one of: personal, org, enterprise, ghes"; exit 1 ;; +esac + +case "${TOKEN_TYPE}" in app|fine-grained|classic) ;; *) + error "--token-type must be one of: app, fine-grained, classic"; exit 1 ;; +esac + +if [[ "${SCOPE}" == "ghes" && -z "${GHES_HOST}" ]]; then + error "--ghes-host is required when --scope ghes"; exit 1 +fi + +# ─── gh CLI setup ───────────────────────────────────────────────────────────── +if [[ "${SCOPE}" == "ghes" ]]; then + export GH_HOST="${GHES_HOST}" + API_BASE="https://${GHES_HOST}/api/v3" + info "Targeting GHES host: ${GHES_HOST}" +else + API_BASE="https://api.github.com" +fi + +FULL_REPO="${OWNER}/${REPO}" + +# ─── main setup ─────────────────────────────────────────────────────────────── +echo "" +echo -e "${BOLD}Changeset Release Flow — Desired State Configuration${RESET}" +echo -e " Repo: ${FULL_REPO}" +echo -e " Scope: ${SCOPE}" +echo -e " Token type: ${TOKEN_TYPE}" +[[ "${DRY_RUN}" == true ]] && echo -e " ${YELLOW}DRY RUN — no changes will be made${RESET}" +echo "" + +# ── Step 1: branch ruleset ──────────────────────────────────────────────────── +header "Step 1/4 — Branch ruleset (protect main)" + +BRANCH_RULESET_NAME="Protect main branch" + +if ruleset_exists "${BRANCH_RULESET_NAME}"; then + success "Branch ruleset '${BRANCH_RULESET_NAME}' already exists — skipping." +else + info "Creating branch ruleset: ${BRANCH_RULESET_NAME}..." + + BRANCH_RULESET_JSON=$(cat <<'ENDJSON' +{ + "name": "Protect main branch", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { "include": ["refs/heads/main"], "exclude": [] } + }, + "rules": [ + { "type": "deletion" }, + { "type": "non_fast_forward" }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 0, + "dismiss_stale_reviews_on_push": false, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_review_thread_resolution": false + } + }, + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": false, + "required_status_checks": [{ "context": "CI Gate" }] + } + } + ] +} +ENDJSON +) + + if [[ "${DRY_RUN}" == true ]]; then + dry "POST repos/${FULL_REPO}/rulesets (branch ruleset payload above)" + else + echo "${BRANCH_RULESET_JSON}" | gh api "repos/${FULL_REPO}/rulesets" \ + --method POST --header "Content-Type: application/json" --input - > /dev/null + success "Branch ruleset created." + fi +fi + +# ── Step 2: tag ruleset ─────────────────────────────────────────────────────── +header "Step 2/4 — Tag ruleset (protect v* tags)" + +TAG_RULESET_NAME="Protect release tags" + +if ruleset_exists "${TAG_RULESET_NAME}"; then + success "Tag ruleset '${TAG_RULESET_NAME}' already exists — skipping." +else + info "Configuring tag ruleset for v* ..." + + if [[ "${TOKEN_TYPE}" == "app" && -n "${APP_ID}" ]]; then + # ── Option A: GitHub App — use App ID directly as the Integration bypass actor ── + # The bypass actor actor_id for an Integration (GitHub App) is the App's numeric ID, + # NOT the installation ID. The App must be installed on the repo (or org) first. + info "GitHub App detected — creating tag ruleset with App ID ${APP_ID} as bypass actor..." + + TAG_RULESET_JSON=$(cat < /dev/null 2>&1; then + success "Tag ruleset created with GitHub App bypass (App ID ${APP_ID})." + else + warning "Tag ruleset creation via API failed — ensure the App is installed on this repo/org." + _tag_ruleset_ui_instructions + fi + fi + + else + # ── Options B & C: PAT — github-actions[bot] bypass requires UI ────────── + warning "Tag ruleset bypass for github-actions[bot] requires the GitHub UI." + _tag_ruleset_ui_instructions + fi +fi + +# ── Step 3: skip-changeset label ────────────────────────────────────────────── +header "Step 3/4 — GitHub label: skip-changeset" + +if label_exists "skip-changeset"; then + success "Label 'skip-changeset' already exists — skipping." +else + info "Creating label 'skip-changeset'..." + + if [[ "${DRY_RUN}" == true ]]; then + dry "POST repos/${FULL_REPO}/labels { name: skip-changeset, color: e4e669, description: ... }" + else + gh api "repos/${FULL_REPO}/labels" \ + --method POST \ + --header "Content-Type: application/json" \ + --field name="skip-changeset" \ + --field color="e4e669" \ + --field description="Opt this PR out of the changeset requirement (docs-only, CI-only, etc.)" > /dev/null + success "Label 'skip-changeset' created." + fi +fi + +# ── Step 4: secrets guidance ────────────────────────────────────────────────── +header "Step 4/4 — Repository secrets" + +_check_secret() { + local secret_name="$1" + # GitHub API returns 200 if secret exists, 404 if not + if gh api "repos/${FULL_REPO}/actions/secrets/${secret_name}" &>/dev/null; then + success "Secret '${secret_name}' already set." + return 0 + fi + return 1 +} + +case "${TOKEN_TYPE}" in + app) + if ! _check_secret "APP_ID" 2>/dev/null; then + echo "" + echo -e " ${BOLD}Action required — set APP_ID secret:${RESET}" + if [[ -n "${APP_ID}" ]]; then + if [[ "${DRY_RUN}" == true ]]; then + dry "gh secret set APP_ID --repo ${FULL_REPO} --body ${APP_ID}" + else + gh secret set APP_ID --repo "${FULL_REPO}" --body "${APP_ID}" + success "APP_ID secret set." + fi + else + echo " Run: gh secret set APP_ID --repo ${FULL_REPO}" + fi + fi + + if ! _check_secret "APP_PRIVATE_KEY" 2>/dev/null; then + echo "" + echo -e " ${BOLD}Action required — set APP_PRIVATE_KEY secret:${RESET}" + if [[ -n "${APP_PEM}" && -f "${APP_PEM}" ]]; then + if [[ "${DRY_RUN}" == true ]]; then + dry "gh secret set APP_PRIVATE_KEY --repo ${FULL_REPO} < ${APP_PEM}" + else + gh secret set APP_PRIVATE_KEY --repo "${FULL_REPO}" < "${APP_PEM}" + success "APP_PRIVATE_KEY secret set." + fi + else + echo " Run: gh secret set APP_PRIVATE_KEY --repo ${FULL_REPO} < /path/to/app.private-key.pem" + fi + fi + ;; + + fine-grained|classic) + if ! _check_secret "CHANGESET_TOKEN" 2>/dev/null; then + echo "" + echo -e " ${BOLD}Action required — set CHANGESET_TOKEN secret:${RESET}" + + case "${TOKEN_TYPE}" in + fine-grained) + case "${SCOPE}" in + personal) + echo " Create at: https://github.com/settings/personal-access-tokens/new" + echo " Resource owner: ${OWNER}" + echo " Repository access: Only select → ${REPO}" + ;; + org|enterprise) + echo " Ensure org allows fine-grained PATs:" + echo " https://github.com/organizations/${OWNER}/settings/personal-access-tokens" + echo " Create at: https://github.com/settings/personal-access-tokens/new" + echo " Resource owner: ${OWNER} (the org, not your personal account)" + echo " Repository access: Only select → ${REPO}" + ;; + ghes) + echo " Create at: https://${GHES_HOST}/settings/personal-access-tokens/new" + echo " Ensure org allows fine-grained PATs (GHES 3.10+ only):" + echo " https://${GHES_HOST}/organizations/${OWNER}/settings/personal-access-tokens" + ;; + esac + echo " Required permissions: Contents (r/w), Pull requests (r/w), Workflows (r/w)" + ;; + classic) + case "${SCOPE}" in + personal|org|enterprise) + echo " Create at: https://github.com/settings/tokens/new" + echo " Scope: repo (includes contents, pull requests, workflows)" + [[ "${SCOPE}" != "personal" ]] && \ + echo " SSO: After creating, authorize for org ${OWNER}" + ;; + ghes) + echo " Create at: https://${GHES_HOST}/settings/tokens/new" + echo " Scope: repo" + ;; + esac + ;; + esac + + echo "" + echo " Then run: gh secret set CHANGESET_TOKEN --repo ${FULL_REPO}" + fi + ;; +esac + +# ── Step 5: immutable releases guidance ─────────────────────────────────────── +header "Immutable releases (manual step)" + +echo -e " Enable at: https://github.com/${FULL_REPO}/settings" +echo -e " Under 'Releases' → check 'Immutable releases'." +echo "" +echo -e " Why it's safe: the CD pipeline uses a draft-first approach." +echo -e " Drafts are never immutable — assets attach freely. The release" +echo -e " only becomes immutable on the final \`gh release edit --draft=false\`." +echo -e " The ERR cleanup trap fires before that line, so failures always" +echo -e " clean up a mutable draft, never a locked release." +echo "" + +# ── Summary ─────────────────────────────────────────────────────────────────── +echo "" +echo -e "${BOLD}${GREEN}════════════════════════════════════════${RESET}" +echo -e "${BOLD}${GREEN} Setup complete for ${FULL_REPO}${RESET}" +echo -e "${BOLD}${GREEN}════════════════════════════════════════${RESET}" +echo "" +echo -e " Verify the setup:" +echo -e " 1. Create a branch, run \`just changeset\`, commit and open a PR" +echo -e " 2. Confirm 'Changeset Check' and 'CI Gate' appear as required checks" +echo -e " 3. Merge the PR; verify a changeset-release/main PR appears" +echo -e " 4. Verify CI runs on the Version PR" +echo -e " 5. Merge the Version PR; verify a GitHub Release is published" +echo ""