Skip to content

Migrate pipeline from CircleCI to GitHub Actions#213

Closed
phelma wants to merge 1 commit into
mainfrom
gha-migration
Closed

Migrate pipeline from CircleCI to GitHub Actions#213
phelma wants to merge 1 commit into
mainfrom
gha-migration

Conversation

@phelma

@phelma phelma commented Jul 16, 2026

Copy link
Copy Markdown

Cutover to GitHub Actions per the Variant B family plan (docker image).
Includes decommission — merging this PR completes the repo's migration.

  • main + pr workflows: loop-guard, check/test, prerelease, release environment gate
  • git-crypt unlock on the runner; encrypted CI GPG key moved to .github/
  • Docker builds/publishes use the runner's native docker daemon (replaces
    CircleCI's setup_remote_docker + install scripts); the integration suite
    builds the image and runs its docker-compose dependencies on the runner
  • Docker Hub credentials load guarded against a locked tree (dynamic block
    detecting the git-crypt \0GITCRYPT header, nil credentials when locked)
    so PR checks run without unlocking git-crypt — the one deliberate change
    inside the image namespace
  • Slack notifications via rake_slack; dependabot auto-merge job
  • Rakefile provisioning swapped to rake_github secrets/environments; rake_circle_ci dropped
  • CircleCI pipeline removed: .circleci/, scripts/ci/, the CI SSH deploy
    key pair and its keys:deploy/deploy_keys provisioning, and the stored
    CircleCI/GitHub API credentials (config/secrets/{circle_ci,github}/)

Deliberate decisions (not defects)

This cutover reproduces the CircleCI pipeline's behaviour, warts included;
fixing inherited hazards is post-migration work. In particular:

  • version:bump[rc]/version:release tag and push before
    image:publish runs — a failed publish leaves a version tag with no image
    behind it. Pre-existing ordering inside the untouched release logic.

  • An rc image is published to Docker Hub on every push to main with no
    approval gate; only full releases are gated (environment: release).

  • version:bump[rc] on a released version increments minor and starts
    rc.1 — every release cycle is a minor bump. Pre-existing
    lib/version.rb logic.

  • Dependabot auto-merge accepts any update type that passes checks, and the
    merge does not trigger a release build — on CircleCI the merge commit
    carried [skip ci], so this matches. Updates ship with the next
    human-triggered release.

  • The old pipeline ran CI only on main and dependabot/* branches;
    pr.yaml running on every pull request is a deliberate family-uniform
    improvement.

  • The release job pulls main at approval time, so a delayed approval
    releases main as it stands then, not the SHA this run tested — parity with
    the old release.sh (which also pulled; prerelease.sh did not, so the
    prerelease job has no pull).

  • Version tags live in git only — there is no version-bump commit, so no
    push-back commit and nothing for the loop-guard to catch; skip-ci-check
    stays as family-uniform belt-and-braces.

  • asdf_install@v1 is our own action (infrablocks/github-actions); we are
    happy tracking its major version tag.

  • Jobs that resolve version tags (test, prerelease, release) check out
    with fetch-depth: 0 — the Rakefile's latest_tag needs the full tag set.

  • The test jobs carry a guard step installing standalone docker-compose
    only if the runner image lacks it (the Rakefile shells out to the
    standalone binary); it is a no-op where compose is present or unused.

  • Job scaffolding is repeated flat per job by design: the logic lives in the
    build system (./go/rake) and CI stays lean — it just triggers tasks and
    supplies secrets/context.

  • Gemfile.lock carries transitive major bumps — the unavoidable resolution
    of the targeted bundle lock --update, not scope creep.

  • Small hunks may appear where the refreshed toolchain's rubocop
    autocorrects existing code — required by the library:check verification
    gate, not drive-by refactoring.

  • Provisioning (pipeline:prepare) authenticates with the operator's ambient
    gh login (GITHUB_TOKEN fallback) instead of a stored PAT — a deliberate
    parity deviation; the stored token in config/secrets/github/config.yaml
    is deleted with the rest of the CircleCI-era credentials.

  • Deleting config/secrets/{circle_ci,github}/ removes the CircleCI API
    token and stored PAT from the tree but does not revoke them — both
    remain live at their issuers and recoverable (git-crypted) from history.
    Revocation is a host-side step on the end-of-migration sweep checklist,
    alongside disabling the CircleCI project and deleting the deploy key.

Do not merge manually — the pipeline merges once checks are green.
Disabling the CircleCI project, deleting the CircleCI deploy key, and
revoking the CircleCI API token + stored PAT are deferred to the
end-of-migration sweep.

@phelma phelma left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Code Review: #213 - Migrate pipeline from CircleCI to GitHub Actions

Verdict: APPROVE

Posted as a COMMENT-event review: GitHub rejects a formal approval from the PR's own author (Can not approve your own pull request). The assessed verdict is APPROVE — zero in-scope defects.

Reviewed through five lenses (correctness, security, safety, standards, code quality) against the Variant B family plan. The diff is fully plan-conformant in both directions — every §4 change is present and correctly shaped, and nothing beyond the plan was touched. I re-ran the plan's §5 self-verification gates independently and all pass. I found no in-scope defects at any severity; every substantive finding below challenges something the plan or PR description documents as deliberate, so they are recorded as plan concerns for a human to revisit at the plan level — they do not block this diff.

Two lens findings were disproven by evidence and are excluded from the counts (see False Positives).

Verification performed

Check Result
rubocop Rakefile ✅ no offenses
rake -T required tasks present; no circle_ci:* / keys:deploy:*
Both workflows parse as YAML
bundle lock under .tool-versions ruby ✅ no-op
PLATFORMS = ruby + x86_64-linux, no darwin
Decommission (.circleci/, scripts/ci/, circle_ci/github secrets gone; ci/ keeps passphrase+gpg, no ssh.*; dockerhub/ untouched)
git grep -i circleci -- ':!Gemfile.lock' ✅ no matches
GPG key move is a pure rename (similarity 100%)
docker-compose pin v5.3.1 + checksum vs published .sha256 asset matches exactly

False Positives (excluded from counts)

  • 🔴→❌ Security — "docker-compose pin references a non-existent release; checksum provenance unverifiable" (major). Disproven. docker/compose v5.3.1 does exist and is the current latest release; I fetched the release's published .sha256 asset and it reads f9ebc6ebdb19d769b793c245a736caaeb198c62587f13b25c660c13b4987f959, matching the workflow byte for byte. The comment's provenance claim ("the checksum is from the release's own .sha256 asset") is accurate. The lens reasoned from a stale assumption that Compose is still on the v2.x line.
  • 🔵→❌ Standards — "double space before trailing channel comments may trip Layout/ExtraSpacing" (suggestion). Disproven. The lens explicitly asked for verification; bundle exec rubocop Rakefile reports no offenses. The prescribed formatting lints clean.

Cross-Cutting Themes

  • Secret blast radius exceeds what each job needs (flagged by: Security ×2) — ENCRYPTION_PASSPHRASE (the git-crypt master key) and the org-wide SLACK_BOT_TOKEN both live in the repo-level Actions store, reachable from any same-repo PR branch's own pr.yaml. Both are plan-specified (§4.4, D2). Requires write access to exploit, so it is a defence-in-depth gap, not an open door — but worth a plan-level decision on environment-scoping.
  • Download-verify-install ordering inverted (flagged by: Security, Safety) — the compose guard writes to its final sudo-owned path before verifying. The checksum is genuine (see above), so this is about ordering robustness only. Plan-specified verbatim; fix belongs fleet-wide.
  • git-crypt header knowledge duplicated in two idioms (flagged by: Correctness, Code Quality) — the \0GITCRYPT check appears twice (return-nil for Docker Hub creds, raise for the passphrase), and the passphrase variant resolves for every github:* task, not just secrets:ensure.

Tradeoff Analysis

  • Parity vs hardening: several lenses independently reached for improvements (scope the passphrase to an environment, put environment: on the privileged release job rather than the slot-free gate, add an update-type filter). Each is a real hardening idea and each is explicitly deferred by the plan's parity principle. Recommendation: keep the diff as-is; route these to the post-migration hardening list.
  • Fail-loud vs fail-quiet: the Docker Hub guard returns nil silently in a locked tree (correct for CI, per plan), while the passphrase guard raises. The asymmetry is deliberate and correct, but costs a local maintainer a confusing Docker denied — see the inline note.

Strengths

  • ✅ Line-accurate implementation of the plan's §4.1/§4.2 YAML — job names, step names and ordering, timeout-minutes (5/10/30), fetch-depth: 0 on exactly the tag-resolving jobs, permissions elevation only where needed, and job-level main-tags concurrency with queue: max.
  • The concurrency reasoning holds under scrutiny: job-level grouping blocks the job before its checkout, so a queued prerelease fetches tags after the preceding run's tag push — latest_tag sees rc.1 and correctly bumps to rc.2 rather than colliding. The gate/slot split genuinely prevents a pending approval freezing all main CI.
  • Script-injection mitigations are correct: github.event.head_commit.message and pull_request.html_url are both passed via env: and quoted, never interpolated into the shell — the single most common vulnerability class in Actions migrations, avoided.
  • ✅ The dynamic credentials guard is correct on the mechanism, not just intent: rake_factory defers the configuration block to task-run time, and rake_docker guards Docker.authenticate!(t.credentials) if t.credentials, so a locked tree's nil is safe.
  • ✅ Choosing the explicit \0GITCRYPT header check over a Psych::SyntaxError rescue is the right call — it keeps genuinely malformed credentials loud in an unlocked release tree instead of silently publishing with nil auth after the tag is pushed.
  • ✅ Slack routing verified against rake_slack's route contract: correct channel IDs, correct first-match-wins order, mandatory catch-all present, and if: ${{ !cancelled() }} aligns with silent_outcomes without double-suppression.
  • ✅ Trust separation: git-crypt unlocks only in prerelease/release; the jobs that execute PR-authored code never see the passphrase or decrypted Docker Hub credentials.
  • ✅ The .tool-versions 3.1.1 → 3.2.11 bump is correctly paired with the activesupport 8.1.3 lock, pre-empting the issue-24 silent-re-resolve trap; bundle lock verified a no-op.
  • ✅ Decommission is complete and the GPG key move is a pure rename.
  • ✅ Every non-obvious decision carries an inline comment explaining why — rare and valuable in CI config.

General Findings (plan concerns)

  • 🔵 Security — the release environment gates ordering, not the privileged job. The maintainers approval sits on release-gate, which runs true and holds no credentials; the release job that holds contents: write, the passphrase and the Docker Hub credentials carries no environment: key. This is the documented gate/slot split and correctly gates sequencing, so it is not a defect — but environment protection rules exert no authority over the privileged job. Worth a comment on the release job noting why it has no environment:, so a future reader doesn't mistake it for an oversight.
  • 🔵 Safety — recovery posture for the tag-then-publish window is undocumented. The ordering hazard is sanctioned, but the recovery path (delete the orphan tag locally and on the remote, re-run) is nowhere written down, and the failure notification to builds gives no hint a tag may need cleaning. One runbook line covers the whole fleet.

Review generated by /accelerator:review-pr

Comment thread .github/workflows/pr.yaml
Comment on lines +60 to +62
permissions:
contents: write
pull-requests: write

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔵 Security / Correctness — plan concern (D3), not a defect of this diff

This job declares permissions: contents: write / pull-requests: write, but for pull_request events triggered by Dependabot GitHub issues a read-only GITHUB_TOKEN regardless of the workflow's permissions: block — the key cannot elevate above the event's ceiling. secrets.GITHUB_TOKEN here also resolves from the Dependabot secret store, which plan §7 deliberately leaves unprovisioned.

Impact: the job fails closed (a 403 on gh pr merge), so there is no vulnerability — but if this is right, D3 auto-merge does not work as specified, fleet-wide. The declared permissions also create the impression write access was granted on a path that runs on every dependabot PR; someone later "fixing" it by reaching for pull_request_target or a PAT would reintroduce exactly the elevated-privilege-on-untrusted-ref pattern this shape avoids.

Suggestion: no change here — this is D3's specified shape. Worth confirming the actual observed behaviour on the pilot repo (issue 06 watch-item territory): if it 403s, the family plan needs a Dependabot-store GH_TOKEN (or dependabot/fetch-metadata + a workflow_run-based merge), and should explicitly rule out pull_request_target as the workaround.

Comment thread Rakefile
Comment on lines +167 to 169
t.secrets = [
{ name: 'ENCRYPTION_PASSPHRASE', value: passphrase.chomp }
]

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔵 Security — plan concern (§4.4 step 3 prescribes this verbatim)

ENCRYPTION_PASSPHRASE is provisioned as a repository-level Actions secret, so it is available to any workflow in the repo — including pr.yaml as modified on a PR branch (same-repo pull_request runs use the PR's own workflow definition and do receive repo secrets). Only prerelease and release need it.

Impact: anyone with write access could open a same-repo PR whose pr.yaml references ${{ secrets.ENCRYPTION_PASSPHRASE }} and exfiltrate the git-crypt master key, decrypting all of config/secrets/** (including Docker Hub credentials) without holding a git-crypt GPG key. This is a mild widening versus CircleCI, where the equivalent var was only reachable on main/dependabot/*pr.yaml running on every PR (a deliberate family improvement) is what changes the exposure surface. Requires existing write access, so: defence-in-depth gap, not an open door. Fork PRs are unaffected.

Suggestion: raise at the family-plan level rather than deviating here — e.g. provision the passphrase into a publish environment's secret store and add environment: publish to prerelease/release. A pr.yaml job cannot reference an environment secret without declaring the environment, which is visible in review.

Comment thread Rakefile
Comment on lines +161 to +165
passphrase = File.binread('config/secrets/ci/encryption.passphrase')
if passphrase.start_with?("\x00GITCRYPT")
raise 'encryption.passphrase is git-crypt ciphertext — unlock the ' \
'clone before provisioning'
end

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔵 Correctness / Code Quality — plan concern (§4.4 step 3 prescribes this verbatim)

This guard sits in the RakeGithub.define_repository_tasks configuration block, which rake_factory invokes at task-run time for every task in the set — not just github:secrets:ensure. So it also fires for github:environments:ensure, which only creates the release environment and has no need of the passphrase. On a locked clone, ./go github:environments:ensure fails with "encryption.passphrase is git-crypt ciphertext" despite the requested operation being unrelated to secrets. File.binread also raises a bare Errno::ENOENT (not the friendly message) if config/secrets/ci/ has not been generated yet.

Impact: low — pipeline:prepare runs both tasks together and plan §7 requires an unlocked clone anyway, so the happy path is unaffected. It only bites an operator invoking the environments task in isolation, who then gets a misleading diagnosis.

Suggestion: leave as-is for family uniformity. If tightened later, wrap the value in dynamic { ... } so the read and guard resolve only when the secrets parameter is actually consumed — the same idiom §4.4 step 2a already uses for the Docker Hub credentials.

Comment on lines +73 to +79
if ! command -v docker-compose >/dev/null 2>&1; then
sudo curl -fsSL \
"https://github.com/docker/compose/releases/download/v5.3.1/docker-compose-linux-x86_64" \
-o /usr/local/bin/docker-compose
echo "f9ebc6ebdb19d769b793c245a736caaeb198c62587f13b25c660c13b4987f959 /usr/local/bin/docker-compose" \
| sha256sum -c -
sudo chmod +x /usr/local/bin/docker-compose

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔵 Security / Safety — plan concern (§4.1/§4.2 prescribe this verbatim)

To be clear up front: the pin itself checks out. I verified docker/compose v5.3.1 exists and that the release's published .sha256 asset reads exactly f9ebc6ebdb19d769b793c245a736caaeb198c62587f13b25c660c13b4987f959 — the comment's provenance claim is accurate. This note is purely about ordering.

curl writes directly to /usr/local/bin/docker-compose and only then pipes the digest to sha256sum -c -. Verify-after-install inverts the safe idiom: the unverified artefact occupies the final, sudo-owned install location on PATH during the verification window.

Impact: low in practice — chmod +x never runs on a failed verification and runners are ephemeral — but any future reordering, retry, or added PATH consumer between the two commands turns a checksum failure into an executed artefact. This step runs as root on runners that later hold ENCRYPTION_PASSPHRASE.

Suggestion: download → verify → install, fleet-wide in the family plan (the same block is in pr.yaml):

tmp=$(mktemp)
curl -fsSL "$URL" -o "$tmp"
echo "$SHA  $tmp" | sha256sum -c -
sudo install -m 0755 "$tmp" /usr/local/bin/docker-compose

Comment on lines +155 to +160
- name: Pull latest main
# Approval can land long after the run starts; release publishes main
# as of approval time, not the tested SHA — parity with the old
# release.sh, which also pulled. prerelease.sh never pulled, so the
# prerelease job deliberately has no pull.
run: git pull

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔵 Correctness — plan concern (§4.2 prescribes this step and its comment verbatim)

This git pull is a guaranteed no-op. The preceding actions/checkout@v4 already uses ref: main with fetch-depth: 0, and it runs after the approval gate and after the job acquires the main-tags slot — so it already fetches main as of approval time. Nothing can land between the checkout and the pull. The behaviour the comment describes ("release publishes main as of approval time, not the tested SHA") is delivered by ref: main, not by the pull; the old release.sh needed the pull only because CircleCI had checked out the triggering SHA.

Impact: no behavioural difference, so nothing is broken. It is dead code adding a small failure surface, and it runs before Set CI git author — in the old pipeline configure-git.sh ran before release.sh's pull, so if a pull ever did need a merge commit it would die with "unable to auto-detect email address" where CircleCI would not. Unreachable today (local main is always equal to or behind origin/main, so the pull fast-forwards at worst).

Suggestion: for the family plan, either drop the step (ref: main already achieves the stated parity) or move Set CI git author above it to match the ordering of the pipeline it reproduces.

Comment thread Rakefile
Comment on lines +227 to +234
t.credentials = dynamic do
if File.binread('config/secrets/dockerhub/credentials.yaml')
.start_with?("\x00GITCRYPT")
nil # tree locked: skip Docker Hub auth (base image is public)
else
YAML.load_file('config/secrets/dockerhub/credentials.yaml')
end
end

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔵 Code Quality — plan concern (§4.4 step 2a prescribes this snippet verbatim)

When the tree is locked this resolves credentials to nil with no diagnostic output. The # tree locked: skip Docker Hub auth comment explains the intent to a reader of the Rakefile, but a maintainer running ./go image:publish in a locked clone locally gets no signal — they see an opaque Docker denied from the push instead.

To be clear, the silent-nil behaviour is correct for CI (it is exactly what lets pr.yaml's test job build without unlocking) — this is only about the local-maintainer experience.

Impact: a locked-tree publish fails with an error pointing at Docker Hub rather than the real cause (the clone was never unlocked), costing debugging time for a condition the code already knows about.

Suggestion: warn 'Docker Hub credentials are git-crypt locked; skipping authentication' on the locked branch before returning nil — cheap, and turns a mystery into a one-line answer. Fleet-wide in the plan if adopted.

Comment thread Rakefile
Comment on lines +228 to +233
if File.binread('config/secrets/dockerhub/credentials.yaml')
.start_with?("\x00GITCRYPT")
nil # tree locked: skip Docker Hub auth (base image is public)
else
YAML.load_file('config/secrets/dockerhub/credentials.yaml')
end

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔵 Code Quality — plan concern (deviates from the plan's verbatim snippets)

The git-crypt ciphertext detection (File.binread(path).start_with?("\x00GITCRYPT")) now appears twice in this Rakefile — here for the Docker Hub credentials, and at the ENCRYPTION_PASSPHRASE guard in the RakeGithub block — in two different shapes (return-nil vs raise). The magic-header constant is duplicated primitive knowledge, and this block additionally reads the same file twice on the unlocked path (binread, then YAML.load_file).

Impact: a future correction (e.g. a different git-crypt header form) must be found in two places, and the duplicated read is an easy inconsistency window.

Suggestion: extract a predicate — def git_crypt_locked?(path) = File.binread(path).start_with?("\x00GITCRYPT") — and use it at both sites. Noting explicitly that this deviates from the plan's prescribed snippets, so it is only worth doing if the family carries the helper fleet-wide; do not fix in this repo alone.

Comment thread .github/workflows/pr.yaml
continue-on-error: true
run: ./go "slack:notify[${{ job.status }}]"
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

🔵 Security — plan concern (D2), post-migration hardening

This step runs ./go "slack:notify[...]" — the PR branch's own Rakefile — with the org-wide SLACK_BOT_TOKEN in its environment. Any same-repo PR branch can therefore read a credential whose blast radius spans the whole organisation's Slack workspace, not just this repo. D2 provisions it org-wide across ~103 repos.

Impact: low likelihood (requires write access, already substantial trust) but wide blast radius. Fork PRs receive no secrets, so external contributors are not a vector.

Suggestion: no change at cutover. Worth recording as post-migration hardening: constrain the Slack bot's scopes to chat:write on the four build channels only, so a leak cannot escalate into broader workspace access.

@phelma
phelma marked this pull request as ready for review July 16, 2026 17:11
Copilot AI review requested due to automatic review settings July 16, 2026 17:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Migrates the repository’s CI/CD pipeline from CircleCI to GitHub Actions, moving build/test/release orchestration into GA workflows and updating the Ruby/Rake tooling to support the new pipeline (including git-crypt/GPG handling and Slack notifications).

Changes:

  • Replace CircleCI pipeline/scripts with GitHub Actions workflows for PR and main (including prerelease/release gating and concurrency controls).
  • Update Rake tasks and dependencies to remove CircleCI-era provisioning and add GitHub/Slack-focused automation.
  • Move the encrypted CI GPG key into .github/ and update documentation accordingly.

Reviewed changes

Copilot reviewed 20 out of 26 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
.github/workflows/pr.yaml Adds PR checks/tests and dependabot auto-merge job.
.github/workflows/main.yaml Adds main-branch CI plus prerelease/release jobs with environment gate and concurrency controls.
Rakefile Switches provisioning from CircleCI to GitHub Actions secrets/environments; adds Slack notification tasks; adds dockerhub-credential guard for locked git-crypt trees.
Gemfile Drops CircleCI/SSH gems; adds rake_factory and rake_slack.
Gemfile.lock Updates dependency set to match Gemfile changes and toolchain refresh.
.tool-versions Updates Ruby version used by the toolchain/CI.
README.md Updates CI key management docs to point to .github/gpg.private.enc.
.github/gpg.private.enc Adds the encrypted CI GPG private key for git-crypt unlock in GA.
.circleci/config.yml Removes CircleCI configuration.
scripts/ci/steps/test.sh Removes CircleCI test step script.
scripts/ci/steps/prerelease.sh Removes CircleCI prerelease step script.
scripts/ci/steps/release.sh Removes CircleCI release step script.
scripts/ci/steps/merge-pull-request.sh Removes CircleCI dependabot merge script.
scripts/ci/common/install-slack-deps.sh Removes CircleCI-specific Slack deps installer.
scripts/ci/common/install-gpg-key.sh Removes CircleCI-specific encrypted GPG key install/decrypt script.
scripts/ci/common/install-git-crypt.sh Removes CircleCI-specific git-crypt installer.
scripts/ci/common/install-docker.sh Removes CircleCI-specific docker installer.
scripts/ci/common/install-docker-compose.sh Removes CircleCI-specific docker-compose installer.
scripts/ci/common/install-asdf.sh Removes CircleCI-specific asdf install script.
scripts/ci/common/install-asdf-dependencies.sh Removes CircleCI-specific asdf dependency install script.
scripts/ci/common/configure-git.sh Removes CircleCI-specific git author configuration script.
scripts/ci/common/configure-asdf.sh Removes CircleCI-specific asdf plugin configuration script.

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

Comment thread .github/workflows/pr.yaml
Comment on lines +64 to +68
- name: Merge pull request
run: gh pr merge --merge "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@phelma phelma closed this Jul 17, 2026
@phelma
phelma deleted the gha-migration branch July 17, 2026 13:57
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.

2 participants