From 7cd19da6db5359ebcdc1bfe8b949546cf5fb905d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Rzepecki?= Date: Wed, 4 Feb 2026 21:15:58 +0100 Subject: [PATCH 1/3] docs: Propose new release pipeline for trusted publishing Documents a proposal for re-architecting the monorepo's release pipeline. This plan addresses the challenges of adopting npm trusted publishing (OIDC) in a multi-package environment with native binary assets. It proposes using stable, "floating" Git tags for client discovery via the GitHub Releases API, eliminating the `npm dist-tag` dance and race conditions. The proposal also includes support for enterprise asset mirroring and mandates checksum verification for enhanced security. --- architecture/trusted-publishing-proposal.md | 152 ++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 architecture/trusted-publishing-proposal.md diff --git a/architecture/trusted-publishing-proposal.md b/architecture/trusted-publishing-proposal.md new file mode 100644 index 0000000000..6ededf1421 --- /dev/null +++ b/architecture/trusted-publishing-proposal.md @@ -0,0 +1,152 @@ +### **Proposal: Modernizing the Release Pipeline with Floating Git Tags** + +#### **1. Objective** + +The primary goal is to re-architect the release process for the AppMap monorepo to enable the use of **npm trusted publishing (OIDC)**. The current process, which relies on a multi-stage "tagging dance" with `npm dist-tag`, is incompatible with OIDC and creates a potential race condition for clients downloading native binaries. + +This proposal outlines a new model using **floating Git tags** as stable pointers to the latest complete releases, decoupling client discovery from the npm registry and creating a more robust, secure, and atomic release pipeline. + +--- + +#### **2. The New Architecture** + +The core of this new architecture is to stop using `npm dist-tag` as a pointer. Instead, we will use the repository's Git tags and the GitHub Releases API as the single source of truth. + +1. **Stable Pointer Tags:** + We will create and maintain stable, "floating" Git tags that do not contain a version number. Each tag will act as a permanent alias for the latest valid release of a specific tool. + * `appmap-latest`: Will always point to the commit of the latest `@appland/appmap-v*` release. + * `scanner-latest`: Will always point to the commit of the latest `@appland/scanner-v*` release. + +2. **Atomic Release Finalization:** + These pointer tags are only moved *after* a new version has been fully released and all its associated native binaries have been successfully built and uploaded to the corresponding GitHub Release. This is the key change that eliminates the race condition. + +3. **Client Discovery:** + The IDE extensions (VS Code, JetBrains) will be updated to find the latest tools by querying a stable GitHub API endpoint. For example, to find the latest AppMap tool, a client would make a `GET` request to: + `https://api.github.com/repos/applandinc/appmap-js/releases/tags/` + + The GitHub API automatically resolves this "floating" tag and returns the full JSON object for the underlying release (e.g., the release for `@appland/appmap-v1.2.3`), which includes the version number, changelog, and a list of all asset download URLs. + +--- + +#### **3. Implementation Plan** + +This is a two-part implementation, requiring changes to the CI/CD workflows and the client-side IDE extensions. + +##### **Part 1: CI/CD Workflow Modifications** + +1. **`release.yml` (JavaScript Publishing Workflow):** + * This workflow will be configured for true **trusted publishing**. + * **Action:** Add `permissions: id-token: write` to the `release` job. + * **Action:** Remove the `YARN_NPM_AUTH_TOKEN` environment variable. The workflow will now authenticate with npm using the OIDC token provided by the GitHub Actions runner. + * `semantic-release` can now publish the JS packages directly with the `latest` dist-tag, as this tag is no longer used by the native asset clients for discovery. + +2. **`build-native.yml` & `build-native-scanner.yml` (Native Binaries Workflows):** + * The primary logic for building and uploading binaries to a GitHub Release remains the same. + * The `finalize-release` job at the end of each workflow will be fundamentally changed. + * **Action:** Remove the step that runs `yarn npm tag add ...`. + * **Action:** Add a new step, "Update Stable Pointer Tag", with the following logic: + + ```yaml + - name: Update Stable Pointer Tag + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + run: | + # Configure Git to use the PAT for pushing + git config user.name "appland-release" + git config user.email "release@app.land" + + # Determine which stable tag to move based on the version tag + STABLE_TAG="" + if [[ "${{ github.ref_name }}" == "@appland/appmap"* ]]; then + STABLE_TAG="appmap-latest" + elif [[ "${{ github.ref_name }}" == "@appland/scanner"* ]]; then + STABLE_TAG="scanner-latest" + else + echo "Not a release tag. Skipping pointer update." + exit 0 + fi + + echo "Updating stable tag '${STABLE_TAG}' to point to '${{ github.ref_name }}'" + + # Use '-f' to move the tag if it already exists + git tag -f "${STABLE_TAG}" "${{ github.ref_name }}" + + # Use --force to push the updated tag to the remote + git push --force origin "refs/tags/${STABLE_TAG}" + ``` + +##### **Part 2: Client-Side Extension Modifications** + +The logic in the VS Code and JetBrains extensions for finding and downloading the native tools needs to be updated. + +1. **Current Logic (To be removed):** + * Shell out to run `npm view @appland/appmap version` to get the latest version string. + * Construct a download URL based on the version string. + +2. **New Logic (To be implemented):** + * Define the stable pointer tag (e.g., `appmap-latest`). + * Make an HTTP GET request to the GitHub API endpoint: `https://api.github.com/repos/applandinc/appmap-js/releases/tags/`. + * Parse the JSON response. The `tag_name` field will give the precise version. + * Iterate through the `assets` array in the JSON response. Find the asset whose name matches the user's platform and architecture (e.g., `appmap-linux-x4`). + * Download the binary from the `browser_download_url` provided for that asset. + * **Action:** After download, calculate the SHA256 checksum of the file and verify that it matches the `digest` field from the manifest. This provides a crucial integrity and security check. + +--- + +#### **4. Benefits of This Architecture** + +* **Enables Trusted Publishing:** Completely removes the final blocker for adopting OIDC for `npm publish`. +* **Atomic Releases:** The pointer tag is only moved after all binaries are confirmed to be uploaded, eliminating any race condition. A client will never be directed to an incomplete release. +* **Increased Resilience:** Client discovery becomes dependent only on the GitHub API, not the npm registry. +* **Single Source of Truth:** The GitHub Release becomes the canonical source for all release artifacts and metadata, simplifying the release process and the client logic. +* **Enhanced Security:** The inclusion of checksum verification provides a strong guarantee of asset integrity, protecting against accidental corruption and providing a second layer of defense. + +--- + +### **5. Advanced Capabilities Unlocked by This Design** + +#### **Enterprise Asset Mirroring** + +This architecture provides a straightforward path for enterprise clients who need to host all tool assets on an internal, mirrored server for security and compliance reasons. + +The JSON response from the GitHub API endpoint effectively serves as a dynamic "manifest." Because the client-side extensions will be designed to fetch from a configurable URL, mirroring can be supported with minimal effort: + +1. **Client-Side Configuration:** The IDE extensions will have a new configuration setting (e.g., `appmap.tool.manifestUrl`) that defaults to the public GitHub API endpoint. +2. **Enterprise Implementation:** An enterprise client can override this URL to point to an internal server. +3. **Mirroring Process:** The client is responsible for a simple mirroring process: + * Periodically fetch the official manifest JSON from the GitHub API. + * Download all binary assets listed in the manifest to their internal artifact server, verifying each one against its checksum. + * Host a modified version of the manifest JSON file. This file **must** preserve the original `digest` field for each asset. The `browser_download_url` for each asset should be updated to point to its new location on the internal mirror. This extends the chain of trust to the mirrored assets. + +#### **Minimal Manifest Structure** + +Clients wishing to create their own mirrored manifest file only need to replicate a small subset of the fields provided by the GitHub Releases API. The client-side parsing logic will only rely on the following structure: + +```json +{ + "tag_name": "@appland/appmap-v1.2.3", + "assets": [ + { + "name": "appmap-linux-x64", + "browser_download_url": "https:///path/to/appmap-linux-x64", + "digest": "sha256:..." + }, + { + "name": "appmap-macos-arm64", + "browser_download_url": "https:///path/to/appmap-macos-arm64", + "digest": "sha256:..." + }, + { + "name": "appmap-win-x64.exe", + "browser_download_url": "https:///path/to/appmap-win-x64.exe", + "digest": "sha256:..." + } + ] +} +``` + +* **`tag_name` (string):** The precise version of the release. This is used for display and diagnostics. +* **`assets` (array of objects):** The list of available binary artifacts. + * **`name` (string):** The unique filename for the asset, used by the client to find the correct binary for its platform and architecture. + * **`browser_download_url` (string):** The fully-qualified URL from which to download the asset. + * **`digest` (string):** The SHA256 checksum of the asset, prefixed with `sha256:`. This is used to verify the integrity of the downloaded file. \ No newline at end of file From 4a31bb7de4748e72ea4704d8b7fdeaaf0c2a425f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Rzepecki?= Date: Thu, 5 Feb 2026 12:05:27 +0100 Subject: [PATCH 2/3] docs: Refine trusted publishing proposal with final details Updates the trusted publishing architecture proposal (`architecture/trusted-publishing-proposal.md`) to incorporate several crucial refinements based on feedback and further analysis: - Changes the architecture to use a dedicated orphan branch instead of a floating tag. - Explicitly notes the removal of the `verifyConditionsCmd` from `.releaserc.js`, which previously checked for `YARN_NPM_AUTH_TOKEN`. - Adds an action item to remove the obsolete generation and uploading of `.sha256` files, as digest verification will now occur via the manifest. --- architecture/trusted-publishing-proposal.md | 169 +++++++++----------- 1 file changed, 76 insertions(+), 93 deletions(-) diff --git a/architecture/trusted-publishing-proposal.md b/architecture/trusted-publishing-proposal.md index 6ededf1421..e1f3b0d169 100644 --- a/architecture/trusted-publishing-proposal.md +++ b/architecture/trusted-publishing-proposal.md @@ -1,105 +1,103 @@ -### **Proposal: Modernizing the Release Pipeline with Floating Git Tags** +### **Proposal: Modernizing the Release Pipeline with an Orphan Manifest Branch** #### **1. Objective** -The primary goal is to re-architect the release process for the AppMap monorepo to enable the use of **npm trusted publishing (OIDC)**. The current process, which relies on a multi-stage "tagging dance" with `npm dist-tag`, is incompatible with OIDC and creates a potential race condition for clients downloading native binaries. - -This proposal outlines a new model using **floating Git tags** as stable pointers to the latest complete releases, decoupling client discovery from the npm registry and creating a more robust, secure, and atomic release pipeline. +To re-architect the release process to enable **npm trusted publishing (OIDC)** by eliminating the dependency on `npm dist-tag` for client discovery. This new architecture will be more robust, eliminate race conditions, and provide powerful new features for both end-users and enterprise clients, such as version pinning and simplified asset mirroring. --- #### **2. The New Architecture** -The core of this new architecture is to stop using `npm dist-tag` as a pointer. Instead, we will use the repository's Git tags and the GitHub Releases API as the single source of truth. +The core of this solution is to use a dedicated, **orphan Git branch** as the single source of truth for release metadata. This branch, named `release-manifests`, will have a separate history from `main` and will contain only JSON manifest files. -1. **Stable Pointer Tags:** - We will create and maintain stable, "floating" Git tags that do not contain a version number. Each tag will act as a permanent alias for the latest valid release of a specific tool. - * `appmap-latest`: Will always point to the commit of the latest `@appland/appmap-v*` release. - * `scanner-latest`: Will always point to the commit of the latest `@appland/scanner-v*` release. +This approach is clean, robust, and leverages a battle-tested pattern for separating source code from generated release assets. -2. **Atomic Release Finalization:** - These pointer tags are only moved *after* a new version has been fully released and all its associated native binaries have been successfully built and uploaded to the corresponding GitHub Release. This is the key change that eliminates the race condition. +##### **Two-Tier Manifest System** -3. **Client Discovery:** - The IDE extensions (VS Code, JetBrains) will be updated to find the latest tools by querying a stable GitHub API endpoint. For example, to find the latest AppMap tool, a client would make a `GET` request to: - `https://api.github.com/repos/applandinc/appmap-js/releases/tags/` +For each successful release of a tool (e.g., `appmap`), the CI process will generate and publish two distinct manifest files to the `release-manifests` branch: - The GitHub API automatically resolves this "floating" tag and returns the full JSON object for the underlying release (e.g., the release for `@appland/appmap-v1.2.3`), which includes the version number, changelog, and a list of all asset download URLs. +1. **Versioned Manifest:** An immutable, permanent manifest named after the full version tag (e.g., `@appland/appmap-v1.2.3.json`). This creates a complete, browsable history of all release metadata. +2. **"Latest" Pointer Manifest:** A "floating" manifest (e.g., `appmap-latest.json`) which is an exact copy of the latest versioned manifest. This file acts as a stable pointer for clients to discover the most recent version. --- #### **3. Implementation Plan** -This is a two-part implementation, requiring changes to the CI/CD workflows and the client-side IDE extensions. - ##### **Part 1: CI/CD Workflow Modifications** 1. **`release.yml` (JavaScript Publishing Workflow):** - * This workflow will be configured for true **trusted publishing**. - * **Action:** Add `permissions: id-token: write` to the `release` job. - * **Action:** Remove the `YARN_NPM_AUTH_TOKEN` environment variable. The workflow will now authenticate with npm using the OIDC token provided by the GitHub Actions runner. - * `semantic-release` can now publish the JS packages directly with the `latest` dist-tag, as this tag is no longer used by the native asset clients for discovery. + * This workflow will be configured for **trusted publishing**. + * **Action:** Add `permissions: id-token: write` to the `release` job and remove the `YARN_NPM_AUTH_TOKEN` environment variable. + * **Action:** Remove the `verifyConditionsCmd` from `.releaserc.js` that previously checked for `YARN_NPM_AUTH_TOKEN`. 2. **`build-native.yml` & `build-native-scanner.yml` (Native Binaries Workflows):** - * The primary logic for building and uploading binaries to a GitHub Release remains the same. - * The `finalize-release` job at the end of each workflow will be fundamentally changed. + * The `finalize-release` job will be responsible for generating and publishing the manifests *after* all binaries have been successfully uploaded to the GitHub Release. + * **Action:** Remove steps that generate and upload `.sha256` files. The digest will now be verified via the manifest. * **Action:** Remove the step that runs `yarn npm tag add ...`. - * **Action:** Add a new step, "Update Stable Pointer Tag", with the following logic: + * **Action:** Add new steps to generate and publish the manifest files. ```yaml - - name: Update Stable Pointer Tag - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} + - name: Generate Release Manifests + id: generate_manifest run: | - # Configure Git to use the PAT for pushing - git config user.name "appland-release" - git config user.email "release@app.land" - - # Determine which stable tag to move based on the version tag - STABLE_TAG="" - if [[ "${{ github.ref_name }}" == "@appland/appmap"* ]]; then - STABLE_TAG="appmap-latest" - elif [[ "${{ github.ref_name }}" == "@appland/scanner"* ]]; then - STABLE_TAG="scanner-latest" - else - echo "Not a release tag. Skipping pointer update." - exit 0 - fi - - echo "Updating stable tag '${STABLE_TAG}' to point to '${{ github.ref_name }}'" - - # Use '-f' to move the tag if it already exists - git tag -f "${STABLE_TAG}" "${{ github.ref_name }}" - - # Use --force to push the updated tag to the remote - git push --force origin "refs/tags/${STABLE_TAG}" + # This is a conceptual script. The actual implementation would query the + # GitHub API for the release assets and construct the JSON. + VERSION_TAG="${{ github.ref_name }}" # e.g., @appland/appmap-v1.2.3 + MANIFEST_DIR="./dist" + + # Extract the base name (e.g., appmap-v1.2.3) by stripping the @appland/ prefix + BASE_TAG="${VERSION_TAG#@appland/}" + + # Determine the tool name (e.g., appmap) from the base tag + TOOL_NAME="${BASE_TAG%%-v*}" + + LATEST_MANIFEST_FILENAME="${TOOL_NAME}-latest.json" + VERSIONED_MANIFEST_FILENAME="${BASE_TAG}.json" + + echo "Generating manifest for ${VERSION_TAG}" + # Pseudocode for manifest generation: + # MANIFEST_CONTENT=$(gh api repos/{owner}/{repo}/releases/tags/${VERSION_TAG} | jq '{...}') + + mkdir -p "${MANIFEST_DIR}" + echo "${MANIFEST_CONTENT}" > "${MANIFEST_DIR}/${VERSIONED_MANIFEST_FILENAME}" + cp "${MANIFEST_DIR}/${VERSIONED_MANIFEST_FILENAME}" "${MANIFEST_DIR}/${LATEST_MANIFEST_FILENAME}" + +- name: Publish Manifests to release-manifests branch + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_branch: release-manifests + publish_dir: ./dist + keep_files: true + user_name: 'appland-release' + user_email: 'release@app.land' + commit_message: 'Update manifest for ${{ github.ref_name }}' + ``` ##### **Part 2: Client-Side Extension Modifications** -The logic in the VS Code and JetBrains extensions for finding and downloading the native tools needs to be updated. +The client logic becomes dramatically simpler and more powerful. -1. **Current Logic (To be removed):** - * Shell out to run `npm view @appland/appmap version` to get the latest version string. - * Construct a download URL based on the version string. +1. **Standard Discovery:** + * To get the latest version, the client makes a single, permanent `GET` request: + `https://raw.githubusercontent.com/getappmap/appmap-js/release-manifests/appmap-latest.json` -2. **New Logic (To be implemented):** - * Define the stable pointer tag (e.g., `appmap-latest`). - * Make an HTTP GET request to the GitHub API endpoint: `https://api.github.com/repos/applandinc/appmap-js/releases/tags/`. - * Parse the JSON response. The `tag_name` field will give the precise version. - * Iterate through the `assets` array in the JSON response. Find the asset whose name matches the user's platform and architecture (e.g., `appmap-linux-x4`). - * Download the binary from the `browser_download_url` provided for that asset. - * **Action:** After download, calculate the SHA256 checksum of the file and verify that it matches the `digest` field from the manifest. This provides a crucial integrity and security check. +2. **Advanced Discovery (Version Pinning):** + * To get a specific version, the client can now construct a URL to its permanent manifest: + `https://raw.githubusercontent.com/getappmap/appmap-js/release-manifests/@appland/appmap-v1.2.3.json` + +3. **Verification:** + * The client will parse the manifest, download the appropriate asset from the `browser_download_url`, and verify its integrity using the SHA256 `digest`. --- #### **4. Benefits of This Architecture** -* **Enables Trusted Publishing:** Completely removes the final blocker for adopting OIDC for `npm publish`. -* **Atomic Releases:** The pointer tag is only moved after all binaries are confirmed to be uploaded, eliminating any race condition. A client will never be directed to an incomplete release. -* **Increased Resilience:** Client discovery becomes dependent only on the GitHub API, not the npm registry. -* **Single Source of Truth:** The GitHub Release becomes the canonical source for all release artifacts and metadata, simplifying the release process and the client logic. -* **Enhanced Security:** The inclusion of checksum verification provides a strong guarantee of asset integrity, protecting against accidental corruption and providing a second layer of defense. +* **Enables Trusted Publishing:** The dependency on `npm dist-tag` is completely removed. +* **Atomic and Race-Free:** Clients will only see a new "latest" manifest *after* all binaries for that release are confirmed to exist. +* **Robust History & Version Pinning:** The two-tier system provides both a stable pointer and a permanent, auditable record for every release, enabling powerful rollback and pinning scenarios. +* **Clean Separation:** The `main` branch history remains clean. Release metadata is logically isolated in its own branch. --- @@ -107,46 +105,31 @@ The logic in the VS Code and JetBrains extensions for finding and downloading th #### **Enterprise Asset Mirroring** -This architecture provides a straightforward path for enterprise clients who need to host all tool assets on an internal, mirrored server for security and compliance reasons. - -The JSON response from the GitHub API endpoint effectively serves as a dynamic "manifest." Because the client-side extensions will be designed to fetch from a configurable URL, mirroring can be supported with minimal effort: +This design makes mirroring trivial and secure. -1. **Client-Side Configuration:** The IDE extensions will have a new configuration setting (e.g., `appmap.tool.manifestUrl`) that defaults to the public GitHub API endpoint. -2. **Enterprise Implementation:** An enterprise client can override this URL to point to an internal server. -3. **Mirroring Process:** The client is responsible for a simple mirroring process: - * Periodically fetch the official manifest JSON from the GitHub API. - * Download all binary assets listed in the manifest to their internal artifact server, verifying each one against its checksum. - * Host a modified version of the manifest JSON file. This file **must** preserve the original `digest` field for each asset. The `browser_download_url` for each asset should be updated to point to its new location on the internal mirror. This extends the chain of trust to the mirrored assets. +* An enterprise client can override a single URL in the extension's settings to point to their own manifest mirror. +* Their mirroring process is simple: + 1. Download the official manifest from `.../appmap-latest.json`. + 2. Download all assets listed in the manifest, verifying each one against its `digest`. + 3. Upload the assets to their internal server. + 4. Host a modified `appmap-latest.json` manifest, updating the `browser_download_url` for each asset but **preserving the original `digest`**. This extends the chain of trust. #### **Minimal Manifest Structure** -Clients wishing to create their own mirrored manifest file only need to replicate a small subset of the fields provided by the GitHub Releases API. The client-side parsing logic will only rely on the following structure: - ```json { "tag_name": "@appland/appmap-v1.2.3", "assets": [ { "name": "appmap-linux-x64", - "browser_download_url": "https:///path/to/appmap-linux-x64", - "digest": "sha256:..." - }, - { - "name": "appmap-macos-arm64", - "browser_download_url": "https:///path/to/appmap-macos-arm64", - "digest": "sha256:..." - }, - { - "name": "appmap-win-x64.exe", - "browser_download_url": "https:///path/to/appmap-win-x64.exe", + "browser_download_url": "https://.../appmap-linux-x64", "digest": "sha256:..." } ] } ``` - -* **`tag_name` (string):** The precise version of the release. This is used for display and diagnostics. -* **`assets` (array of objects):** The list of available binary artifacts. - * **`name` (string):** The unique filename for the asset, used by the client to find the correct binary for its platform and architecture. - * **`browser_download_url` (string):** The fully-qualified URL from which to download the asset. - * **`digest` (string):** The SHA256 checksum of the asset, prefixed with `sha256:`. This is used to verify the integrity of the downloaded file. \ No newline at end of file +* **`tag_name`:** The precise version string. +* **`assets`:** An array of release artifacts. + * **`name`:** The unique filename. + * **`browser_download_url`:** The direct download URL. + * **`digest`:** The SHA256 checksum, prefixed with `sha256:`. \ No newline at end of file From a5b214bbbb066d7e6ae6c2a4aa67899e74671b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Rzepecki?= Date: Thu, 5 Feb 2026 12:45:37 +0100 Subject: [PATCH 3/3] fixup: Fix small issues in the proposal --- architecture/trusted-publishing-proposal.md | 39 ++++++++++++--------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/architecture/trusted-publishing-proposal.md b/architecture/trusted-publishing-proposal.md index e1f3b0d169..805840e5ea 100644 --- a/architecture/trusted-publishing-proposal.md +++ b/architecture/trusted-publishing-proposal.md @@ -16,7 +16,7 @@ This approach is clean, robust, and leverages a battle-tested pattern for separa For each successful release of a tool (e.g., `appmap`), the CI process will generate and publish two distinct manifest files to the `release-manifests` branch: -1. **Versioned Manifest:** An immutable, permanent manifest named after the full version tag (e.g., `@appland/appmap-v1.2.3.json`). This creates a complete, browsable history of all release metadata. +1. **Versioned Manifest:** An immutable, permanent manifest named after the full version tag (e.g., `appmap-v1.2.3.json`). This creates a complete, browsable history of all release metadata. 2. **"Latest" Pointer Manifest:** A "floating" manifest (e.g., `appmap-latest.json`) which is an exact copy of the latest versioned manifest. This file acts as a stable pointer for clients to discover the most recent version. --- @@ -26,15 +26,22 @@ For each successful release of a tool (e.g., `appmap`), the CI process will gene ##### **Part 1: CI/CD Workflow Modifications** 1. **`release.yml` (JavaScript Publishing Workflow):** + * Extracted from current `main.yml` workflow for clarity and better permission scoping; trigerred on successful build on the main branch. * This workflow will be configured for **trusted publishing**. - * **Action:** Add `permissions: id-token: write` to the `release` job and remove the `YARN_NPM_AUTH_TOKEN` environment variable. + * **Action:** Configure the `release` job with an explicit `permissions` block for OIDC, and remove the `YARN_NPM_AUTH_TOKEN` environment variable. For example: + + ```yaml + permissions: + id-token: write + contents: read + ``` * **Action:** Remove the `verifyConditionsCmd` from `.releaserc.js` that previously checked for `YARN_NPM_AUTH_TOKEN`. 2. **`build-native.yml` & `build-native-scanner.yml` (Native Binaries Workflows):** * The `finalize-release` job will be responsible for generating and publishing the manifests *after* all binaries have been successfully uploaded to the GitHub Release. * **Action:** Remove steps that generate and upload `.sha256` files. The digest will now be verified via the manifest. * **Action:** Remove the step that runs `yarn npm tag add ...`. - * **Action:** Add new steps to generate and publish the manifest files. + * **Action:** Add new steps to generate and publish the manifest files. (Note: `contents: write` permission required.) ```yaml - name: Generate Release Manifests @@ -55,24 +62,22 @@ For each successful release of a tool (e.g., `appmap`), the CI process will gene VERSIONED_MANIFEST_FILENAME="${BASE_TAG}.json" echo "Generating manifest for ${VERSION_TAG}" - # Pseudocode for manifest generation: - # MANIFEST_CONTENT=$(gh api repos/{owner}/{repo}/releases/tags/${VERSION_TAG} | jq '{...}') + MANIFEST_CONTENT=$(gh api repos/:owner/:repo/releases/tags/${VERSION_TAG} | jq '{tag_name, assets: [.assets[] | {name, browser_download_url, digest}]}') mkdir -p "${MANIFEST_DIR}" echo "${MANIFEST_CONTENT}" > "${MANIFEST_DIR}/${VERSIONED_MANIFEST_FILENAME}" cp "${MANIFEST_DIR}/${VERSIONED_MANIFEST_FILENAME}" "${MANIFEST_DIR}/${LATEST_MANIFEST_FILENAME}" -- name: Publish Manifests to release-manifests branch - uses: peaceiris/actions-gh-pages@v4 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_branch: release-manifests - publish_dir: ./dist - keep_files: true - user_name: 'appland-release' - user_email: 'release@app.land' - commit_message: 'Update manifest for ${{ github.ref_name }}' - + - name: Publish Manifests to release-manifests branch + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_branch: release-manifests + publish_dir: ./dist + keep_files: true + user_name: 'appland-release' + user_email: 'release@app.land' + commit_message: 'Update manifest for ${{ github.ref_name }}' ``` ##### **Part 2: Client-Side Extension Modifications** @@ -85,7 +90,7 @@ The client logic becomes dramatically simpler and more powerful. 2. **Advanced Discovery (Version Pinning):** * To get a specific version, the client can now construct a URL to its permanent manifest: - `https://raw.githubusercontent.com/getappmap/appmap-js/release-manifests/@appland/appmap-v1.2.3.json` + `https://raw.githubusercontent.com/getappmap/appmap-js/release-manifests/appmap-v1.2.3.json` 3. **Verification:** * The client will parse the manifest, download the appropriate asset from the `browser_download_url`, and verify its integrity using the SHA256 `digest`.