Skip to content

Dual-publish releases to public npm and the internal copilot-canary feed - #23

Merged
MackinnonBuck merged 6 commits into
mainfrom
mackinnonbuck-canary-feed-publish
Jul 28, 2026
Merged

Dual-publish releases to public npm and the internal copilot-canary feed#23
MackinnonBuck merged 6 commits into
mainfrom
mackinnonbuck-canary-feed-publish

Conversation

@MackinnonBuck

@MackinnonBuck MackinnonBuck commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Mirrors every release to the internal Azure Artifacts copilot-canary feed in addition to public npm — the same version and the same tarball, not a separate canary build — so internal consumers that can't pull from public npm can install the identical build. Modeled on copilot-agent-runtime's publish-cli.yml.

Verified end-to-end: run 30403775246 published to the feed through this PR's actual publish-internal job (byte-identical, on a throwaway branch using a disposable package name). Public npm was untouched. The npm publish path itself is unchanged in substance and can't be dry-run.

Changes

  • Four jobsversionpublish → {publish-internal, release}. version is isolated so a retry reuses the resolved version instead of reading the now-higher published one and cutting a different release.
  • Pack once, publish twicepublish uploads the .tgz as an artifact; publish-internal publishes that byte-identical tarball to the feed. Publishing a tarball also skips prepare/prepublishOnly, so nothing rebuilds with feed credentials in scope.
  • Feed auth via OIDC — SHA-pinned azure/login, az account get-access-token, masked, written to an auth-only .npmrc. It deliberately omits a scoped @github:registry= line, which is what keeps the explicit --registry authoritative in both directions.
  • script/npm-publish-idempotent.sh (new) — ported byte-for-byte from the runtime; treats an "already published" conflict as success so re-runs are safe. Used for both publishes.
  • Integrity check after the npm publish — a conflict alone doesn't prove this run's tarball landed. Without it, such a run would exit 0 and mirror a different artifact to the feed and the release under the same version.
  • .gitattributes (new) — keeps the shell script LF on Windows checkouts.

Provisioning

Nothing new to create — both publish jobs reuse the existing npm-publish environment. Already in place and confirmed by the run above: repo variables CPD_ID_CLIENT_ID / CPD_ID_TENANT_ID, a federated credential on id-cpd-ci with subject repo:github/copilot-engine-sdk:environment:npm-publish, and Feed Publisher on copilot-canary.

Publishes @github/copilot-engine-sdk canary builds to the internal Azure Artifacts copilot-canary feed only, never public npm. Adds .github/workflows/canary-publish.yml with fork guard, feed-only publish guards, prerelease version scheme, and idempotent publish, plus README docs for consuming from the feed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b1cc5615-75ab-477b-9dbd-feff9d63a426
Copilot AI review requested due to automatic review settings July 28, 2026 17:44
@MackinnonBuck
MackinnonBuck requested a review from a team as a code owner July 28, 2026 17:44
@MackinnonBuck
MackinnonBuck marked this pull request as draft July 28, 2026 17:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an internal-only Azure Artifacts canary publishing path and consumer setup instructions.

Changes:

  • Adds manual canary build and publishing workflow.
  • Implements feed authentication, versioning, and publish safeguards.
  • Documents internal canary installation.
Show a summary per file
File Description
.github/workflows/canary-publish.yml Builds and publishes canary packages.
README.md Documents internal feed consumption.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 2/2 changed files
  • Comments generated: 4
  • Review effort level: Medium

Comment thread .github/workflows/canary-publish.yml Outdated
Comment thread .github/workflows/canary-publish.yml Outdated
Comment thread .github/workflows/canary-publish.yml Outdated
Comment thread README.md Outdated
Rework publish.yml into two jobs mirroring copilot-agent-runtime's publish-cli.yml: a publish job (public npm, environment npm-publish) that packs once and uploads the tarball artifact, and a publish-internal job (environment cicd) that publishes the identical tarball to the copilot-canary feed via OIDC with an explicit --registry guard. Port script/npm-publish-idempotent.sh for retry-safe publishes, drop the standalone canary workflow, add .gitattributes to keep the script LF, and reframe the README section as an internal-feed mirror.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b1cc5615-75ab-477b-9dbd-feff9d63a426
Copilot AI review requested due to automatic review settings July 28, 2026 18:02
@MackinnonBuck MackinnonBuck changed the title Add canary publish workflow for internal copilot-canary feed Dual-publish releases to public npm and the internal copilot-canary feed Jul 28, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (3)

.github/workflows/publish.yml:117

  • This does not make retries of the public job idempotent. A failed-job re-run starts from the beginning; if npm publication succeeded and the later GitHub release step failed, npm view now sees that new version and the workflow increments it again, so the conflict handler is never exercised (and, once artifact re-upload is fixed, the retry publishes another irreversible version). Persist/reuse the originally selected version and tarball across attempts, or split preparation, npm publication, and GitHub release into dependent jobs so successful stages are not rerun.
          # Idempotent so a re-run of a failed job doesn't fail on an already
          # published version. Explicit --registry keeps the target unambiguous.
          if bash script/npm-publish-idempotent.sh --run "$TARBALL" \

.github/workflows/publish.yml:107

  • upload-artifact@v4 rejects a second upload with the same name in one workflow run by default. If this job is re-run after the artifact was uploaded but a later step failed, it will stop here with a 409 instead of reaching the idempotent publish logic. Allow replacement of the prior attempt's artifact.

This issue also appears on line 115 of the same file.

          name: npm-tarball
          path: ${{ steps.pack.outputs.tarball }}
          if-no-files-found: error
          retention-days: 7

README.md:40

  • The “npm (above)” reference is incorrect: the preceding installation section only shows a GitHub dependency and never gives the public npm command. State the npm install command directly so public consumers can follow these instructions.
Every release published by `.github/workflows/publish.yml` is mirrored — the
exact same version — to the internal Azure Artifacts `copilot-canary` feed in
addition to public npm. Public consumers should just use npm (above); internal
consumers that need to resolve `@github/copilot-engine-sdk` from the feed can
point the `@github` scope at it and authenticate. Add an `.npmrc` next to your
`package.json`:
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

Addresses review: computing the version inside the publish job broke retry-safety and the same-version-everywhere invariant (a re-run would read the now-higher public version and publish a different one). Restructure into version -> publish -> {publish-internal, release}, mirroring copilot-agent-runtime's dedicated version job. Add overwrite: true to the tarball upload so the publish job is re-runnable in the same run, and make the GitHub release idempotent (skip if the tag exists). publish-internal and release are now independent so neither blocks the other.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b1cc5615-75ab-477b-9dbd-feff9d63a426
Copilot AI review requested due to automatic review settings July 28, 2026 18:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (2)

.github/workflows/publish.yml:280

  • If gh release create creates the release but fails while uploading the tarball, a retry enters this branch and reports success while leaving the release incomplete. Upload the artifact with --clobber when the release already exists so partial failures are repaired.
          if gh release view "$TAG" >/dev/null 2>&1; then
            echo "Release $TAG already exists; skipping creation." >> "$GITHUB_STEP_SUMMARY"
            exit 0
          fi

README.md:44

  • A scoped registry applies to every @github/* dependency, not just this SDK, so this comment incorrectly says all other packages keep using the default registry. Clarify the boundary to prevent consumers from overlooking that other @github packages will also be routed to this feed.
# All other packages continue to resolve from your default registry.
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

The feed section was carried over from the earlier canary-only design, where the feed was the sole way to obtain a build and documenting its auth flow was load-bearing. Under dual-publish the feed receives the exact same version as public npm, so npm install covers effectively every consumer and the section documented a generic feed auth flow this package doesn't need.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b1cc5615-75ab-477b-9dbd-feff9d63a426
Copilot AI review requested due to automatic review settings July 28, 2026 19:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (1)

.github/workflows/publish.yml:280

  • This skip makes release retries incomplete. gh release create creates the release before uploading its asset, so an upload failure can leave an existing release with no tarball; the retry then exits here permanently. Check the existing release's assets and upload the downloaded tarball when it is missing.
          if gh release view "$TAG" >/dev/null 2>&1; then
            echo "Release $TAG already exists; skipping creation." >> "$GITHUB_STEP_SUMMARY"
            exit 0
          fi
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread script/npm-publish-idempotent.sh
@MackinnonBuck

This comment was marked as low quality.

The idempotent publish wrapper treats an already-published version as
success so retries are safe, but a conflict alone does not prove the
registry holds this run's tarball. Assert the published dist.integrity
matches the local tarball so publish-internal and release cannot
propagate a different artifact under the same version.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b1cc5615-75ab-477b-9dbd-feff9d63a426
Copilot AI review requested due to automatic review settings July 28, 2026 20:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (2)

.github/workflows/publish.yml:250

  • The idempotent wrapper can report success when this version already exists, but this internal path never confirms that the existing feed entry contains this tarball. A stale or out-of-band package can therefore make the workflow claim the mirror succeeded while the feed serves different bytes. As in the public publish path, compare the feed's dist.integrity with the downloaded tarball before reporting success.
          if bash "$GITHUB_WORKSPACE/script/npm-publish-idempotent.sh" --run "${tarballs[0]}" \
            --registry "$FEED_URL" --tag latest; then

.github/workflows/publish.yml:301

  • Skipping solely because the release exists is not retry-safe. gh release create creates the release before uploading its assets (using a draft during upload), so an upload failure can leave an existing draft or published release without this tarball; the retry then exits successfully without repairing it. Re-upload the asset with --clobber and publish any leftover draft before skipping creation.
          if gh release view "$TAG" >/dev/null 2>&1; then
            echo "Release $TAG already exists; skipping creation." >> "$GITHUB_STEP_SUMMARY"
            exit 0
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

Creating a dedicated environment requires repo admin access. Two jobs can
reference the same environment, so scoping the id-cpd-ci federated
credential to repo:github/copilot-engine-sdk:environment:npm-publish
removes that dependency and leaves only the identity admin's claim.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b1cc5615-75ab-477b-9dbd-feff9d63a426
Copilot AI review requested due to automatic review settings July 28, 2026 20:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Comments suppressed due to low confidence (2)

.github/workflows/publish.yml:253

  • The conflict wrapper can return success when this version already exists internally, but this job never verifies that the feed's existing dist.integrity matches the downloaded tarball. A pre-existing version with different bytes would therefore be reported as successfully mirrored, violating the workflow's exact-tarball invariant. Please perform the same authenticated integrity check used for public npm before marking this job successful.
          if bash "$GITHUB_WORKSPACE/script/npm-publish-idempotent.sh" --run "${tarballs[0]}" \
            --registry "$FEED_URL" --tag latest; then
            echo "Published internally (or already existed): @github/copilot-engine-sdk@${VERSION}" >> "$GITHUB_STEP_SUMMARY"

.github/workflows/publish.yml:304

  • gh release create creates the release before uploading assets, so an asset-upload failure can leave the release present while the job fails. On a retry this branch exits successfully without restoring the missing tarball, making the new retry path incomplete. Upload (or verify) the tarball on the existing-release path before returning success.
          if gh release view "$TAG" >/dev/null 2>&1; then
            echo "Release $TAG already exists; skipping creation." >> "$GITHUB_STEP_SUMMARY"
            exit 0
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@MackinnonBuck
MackinnonBuck marked this pull request as ready for review July 28, 2026 22:19
@MackinnonBuck
MackinnonBuck merged commit 6359a96 into main Jul 28, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants