npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gowalk-cicd

v1.0.3

Published

Zero-config GitHub Actions delivery for iOS TestFlight and Android Google Play.

Readme

gowalk-cicd

One installer for iOS TestFlight and Android Google Play delivery. It supports native Swift/SwiftUI iOS projects and Flutter apps. Put the platform keys under creds/, push to main, and both store builds run only on GitHub Actions.

Install

From the root of your app repo:

npx --yes gowalk-cicd

Writes three things into your repo:

  • .github/actions/swift-app/ — the vendored composite action (action.yml + scripts)
  • .github/actions/android-app/ — Flutter Android build/sign action
  • .github/workflows/deploy.yml — workflow that builds and deploys both platforms

Re-run the same command anytime to pull the latest version.

iOS credentials

  1. Create an App Store Connect API key with the App Manager role (App Store Connect → Users and Access → Keys → generate).
  2. Download the .p8 and save it into your repo at:
    creds/AuthKey_<KEY_ID>_Issuer_<ISSUER_UUID>.p8
    Example: creds/AuthKey_ABC123DEFG_Issuer_69a6de70-xxxx-47e3-e053-5b8c7c11a4d1.p8 The filename encodes both the KEY_ID and the ISSUER_ID — the composite action parses them from the filename.
  3. The repo must be private. The p8 is a long-lived credential; never commit it to a public repo.
  4. (First time only, per app) Create the app record in App Store Connect. See First-time app setup below.
  5. The existing metadata-rich form *(key_id_<KEY_ID>_issuer_<UUID>_vendor_id_<ID>).p8 is also accepted.

Android credentials

Place these files under creds/:

  1. android-upload-key.jks — the long-lived Google Play upload keystore.

  2. android-signing.properties — signing metadata in this exact format:

    storeFile=android-upload-key.jks
    storePassword=<keystore password>
    keyAlias=<upload key alias>
    keyPassword=<upload key password>
  3. One Google service-account JSON key. Its filename is unrestricted (Gowalk.json is valid); the action identifies it by the type: service_account JSON fields.

The service account must have Google Play Console access to the app and the Android Publisher API must be enabled. Keep the repository private. The workflow reads these files from the checkout, so they must be available to GitHub Actions (this package's zero-config convention is to track them only in a private repository).

Google Play requires the first AAB to be uploaded through Play Console. The first CI run still succeeds and retains the signed AAB as an android-<package>-<versionCode> workflow artifact. Upload that artifact once in Play Console; every later run detects API readiness and uploads to the internal track automatically. Override the track or status with repository variables GOOGLE_PLAY_TRACK and GOOGLE_PLAY_STATUS.

Android delivery via Bitrise (alternative mode)

Use this when the Android upload keystore password lives in Bitrise (not in the repo) — the GitHub runner then cannot sign the AAB locally. In this mode the Android job triggers a Bitrise workflow (which holds the keystore + Play service-account secrets) over the Bitrise API and waits for it, mirroring the build result back into the GitHub Actions run. iOS still ships via GitHub Actions as normal.

Enable it by committing creds/bitrise.json:

{
  "enabled": true,
  "app_slug": "8b2a7a24-....",
  "workflow": "android-deploy",
  "branch": "main",
  "api_token": "bitpat_...."
}
  • app_slug — the Bitrise app slug (from the app's Bitrise URL or GET https://api.bitrise.io/v0.1/apps).
  • workflow — the Bitrise workflow that builds, signs, and runs google-play-deploy (default android-deploy).
  • branch — optional; defaults to the pushed ref.
  • api_token — a Bitrise personal access token. Committing it to a private repo's creds/ follows the same trust model as the ASC .p8. For non-private repos, omit it here and set the BITRISE_API_TOKEN Actions secret instead.

When creds/bitrise.json is present with enabled: true, the local build/sign/upload steps are skipped and bitrise_deploy.py drives the Bitrise build; otherwise the local path above runs unchanged.

The Bitrise android-deploy workflow must produce a versionCode higher than what is already on Play. The convention used by this fleet is a +200 offset over $BITRISE_BUILD_NUMBER via a change-android-versioncode-and-versionname step, with the versionName read from pubspec.yaml.

Sharing one distribution cert across a team's apps

The iOS action caches the Apple Distribution cert under creds/ and reuses it across runs (cert alive + NotAfter > 30 days). But Apple caps a team at 2 distribution certs, and the cache is per-repo. If three or more apps share one Apple Developer team, they cannot each hold their own cert — every run finds its cached cert already revoked by a sibling, mints a new one, and revokes another. The symptom is a "Your Certificate Has Been Revoked" email on nearly every deploy.

Fix: apps in the same Apple team should share one cert. Commit the same creds/cert.p12 + creds/cert.meta.json into every repo of that team (an Apple Distribution cert is team-scoped, not app-scoped, so one cert signs them all). Each app then gets a cache hit and no new cert is ever created. Provisioning profiles stay per-app (named CI-<bundle_id>) and regenerate against the shared cert on the next run. When onboarding a new app into an existing team, copy a sibling's cert.p12 + cert.meta.json in rather than letting the first run create a fresh cert.

How it works

On every push, iOS and Android run in parallel. Flutter is installed only on the GitHub-hosted runners. The Android job runs flutter analyze, flutter test, builds an AAB with GITHUB_RUN_NUMBER as its monotonically increasing version code, replaces any development signature with the upload key, and retains the signed bundle before contacting Google Play.

iOS delivery

The iOS composite action runs on macos-15 and:

  1. Auto-detects your .xcodeproj / .xcworkspace, scheme, bundle ID, and team_id (from the ASC API key). No ci.config.yaml required — override via action inputs only if auto-detection fails.
  2. Reads the ASC key from creds/AuthKey_*.p8 and uses it to authenticate to App Store Connect via JWT.
  3. Decides the marketing version: either reuses the current PREPARE_FOR_SUBMISSION version on App Store Connect, or creates a new version if the highest declared version is already live.
  4. Computes the next build number by querying ASC for the latest uploaded build and incrementing.
  5. Provisions signing at runtime: generates a throwaway Apple Distribution cert + a per-target App Store provisioning profile named CI-<bundle_id>. Patches the .pbxproj to use Manual signing against those profiles.
  6. Archives with xcodebuild archive, exports the IPA, and uploads via xcrun altool.
  7. Sets "What's New" on every declared localization (reads fastlane/metadata/ios/<locale>/release_notes.txt if present, or from the app-store-whats-new input).
  8. Auto-fills empty App Store metadata (name, subtitle, keywords, description, promotional text, what's new) via GitHub Models AI, on every locale that has gaps. See AI metadata auto-fill below.

The iOS action requires only the p8. Everything else is derived.

AI metadata auto-fill

On every run, after the TestFlight upload succeeds, the action:

  1. Queries App Store Connect for every appInfoLocalization and appStoreVersionLocalization on the editable version.
  2. Computes the set of empty fields per locale (URL fields are always skipped — you must set those manually in ASC).
  3. Scans your repo for context (README, Info.plist, dependency files, top Swift files) and feeds it to openai/gpt-4o via GitHub Models with a strict JSON schema.
  4. PATCHes only the fields that were empty — never overwrites existing content.

Fully idempotent: a second run with no empty fields skips the AI step entirely (zero requests, zero PATCHes).

Requirement: permissions: models: read

actions/ai-inference needs the models: read permission. The template workflow written by npx gowalk-cicd already includes it:

permissions:
  contents: read
  models: read

Existing consumers must add this block to their deploy.yml at the workflow or job level. If it's missing, the AI step fails open with a ::warning:: and the rest of the workflow continues unaffected.

Rate limits

GitHub Models free tier allows 50 gpt-4o requests per day (10 per minute). One workflow run = one request. If you run many apps from the same GitHub account, or trigger several builds per day, switch to the cheaper mini model:

- uses: ./.github/actions/swift-app
  with:
    ai-metadata-model: openai/gpt-4o-mini

gpt-4o-mini has a much higher free-tier quota.

Disabling AI metadata

Pass ai-metadata: 'false' to skip the AI steps entirely:

- uses: ./.github/actions/swift-app
  with:
    ai-metadata: 'false'

Update

npx --yes gowalk-cicd

Overwrites both vendored actions and .github/workflows/deploy.yml with the latest versions. Because the actions are vendored locally, builds use the reviewed copy in your app repository; updates arrive through this npm package.

Override config (rare)

Most projects never need this. If auto-detection fails or you have multiple schemes, pass inputs in .github/workflows/deploy.yml:

- uses: ./.github/actions/swift-app
  with:
    scheme: MyAppRelease
    bundle-id: com.example.myapp
    run-tests: 'false'
    uses-non-exempt-encryption: 'false'

All inputs are declared in .github/actions/swift-app/action.yml. The common ones:

| Input | Purpose | |-------|---------| | project / workspace | Path to .xcodeproj or .xcworkspace | | scheme | Xcode scheme to archive | | configuration | Release (default) or custom | | bundle-id | Override the auto-detected bundle identifier | | team-id | Override the auto-detected team ID | | app-store-apple-id | Numeric ASC app ID (override auto-lookup) | | run-tests | false to skip the simulator test stage | | uses-non-exempt-encryption | Value for ITSAppUsesNonExemptEncryption | | archive | false to build-only (PR runs without secrets) | | upload | false to archive but not upload to TestFlight | | app-store-whats-new | Inline "What's New" text (overrides files) | | ai-metadata | false to disable AI auto-fill of empty ASC metadata | | ai-metadata-model | GitHub Models model id (default openai/gpt-4o) |

First-time app setup

The app record must exist in App Store Connect before the first CI upload. Create it once in the App Store Connect web interface with the same bundle ID. Do not run store deployment tools locally; builds, signing, and uploads remain inside GitHub Actions.

Once the app exists, all subsequent builds and uploads are fully automated via the ASC API key.

Troubleshooting

"No app found for bundle ID" — the app record doesn't exist yet. Run the first-time setup above.

"MARKETING_VERSION is not set" — the action requires MARKETING_VERSION to be declared in your target's build settings. Open the target in Xcode → Build Settings → Versioning → set MARKETING_VERSION (and CURRENT_PROJECT_VERSION) to $(MARKETING_VERSION) / $(CURRENT_PROJECT_VERSION) respectively.

"You must accept the latest Program License Agreement" — go to developer.apple.com and App Store Connect as the account holder, accept any pending agreements, retry.

Upload fails with provisioning errors — delete any stale profiles named CI-<bundle_id> on developer.apple.com and re-run; the action will regenerate.

Auto-bumping MARKETING_VERSION

When the ASC combined floor (max of pending review, preReleaseVersions, or builds-via-preReleaseVersion) exceeds your project's MARKETING_VERSION, the action auto-bumps and commits the new value as part of the same bot commit that handles cert refresh / autoupdate.

Default policy is rollover — patch with carry: at .9 it rolls into the next minor (1.0.91.1.0), and at minor=9 it cascades into the next major (1.9.92.0.0). Major has no upper limit (9.9.910.0.0). This produces the more natural human progression most projects want — patch numbers never silently grow past 9.

Four policies are supported:

| Policy | Example bump | When to use | |--------|--------------|-------------| | rollover (default) | 1.0.51.0.6; 1.0.91.1.0 | Natural progression, carry at .9. | | patch | 1.0.51.0.6; 1.0.91.0.10 | Legacy unbounded patch — pinned for backward compat. | | minor | 1.0.51.1.0; 1.0.91.1.0 | Projects that ship every release as a minor. | | none | (fails the build) | Explicit semver control via human bump. |

Full rollover behaviour: 1.0.91.1.0 (patch overflow), 1.9.92.0.0 (minor cascade), 9.9.910.0.0 (major no upper limit).

Backward compat: existing consumers on 0.0.27 that explicitly pin marketing-version-auto-bump: 'patch' keep their current unbounded behavior — the 'patch' policy is unchanged. The default change from 'patch''rollover' only affects new installs and consumers that do not override the input.

Opt out via the action input:

- uses: ./.github/actions/swift-app
  with:
    marketing-version-auto-bump: 'none'

In 'none' mode, the floor check fails the build and you must bump MARKETING_VERSION manually before retrying.

Side effect: the bot commit subject reflects what was changed, e.g. ci: refresh signing identity + bump MARKETING_VERSION [skip ci].

Source-of-truth resolution. The auto-bump writes the new value into the file your project actually reads from, in this order:

  1. xcodegen project.yml (preferred when present): regex-rewrite of the MARKETING_VERSION: key, preserving formatting. The generated *.xcodeproj is regenerated on every build, so editing it directly would lose the bump.
  2. *.xcconfig sitting alongside the project: handles non-xcodegen projects that hoist MARKETING_VERSION into xcconfig.
  3. *.xcodeproj/project.pbxproj: only when no xcodegen spec is present.
  4. Info.plist CFBundleShortVersionString: last-ditch fallback.

If your project uses xcodegen but MARKETING_VERSION lives somewhere not in project.yml or .xcconfig, the action emits a ::warning:: and falls back to fail-on-floor (refusing to silently edit the generated pbxproj). Either move MARKETING_VERSION under settings.base in project.yml, or pin marketing-version-auto-bump: 'none' and bump manually.

Auto-updates

The vendored action ships with a per-run autoupdate check. On every push to your default branch, the action queries npm for the latest gowalk-cicd, compares against the local marker at .github/actions/swift-app/.daemux-version, and if newer, re-vendors the package via npx --yes and commits the refreshed action files back (under both .github/actions/swift-app/ and .github/actions/android-app/). .github/workflows/deploy.yml is NEVER auto-committed — see "deploy.yml is not auto-updated" below.

| Aspect | Behaviour | |--------|-----------| | Trigger | Push to default branch only (PR / branch runs do nothing) | | Lag | One run — the next push after a new release picks up the update | | Suppression | [skip ci] in the commit subject + paths-ignore for both action directories | | Combined commit | Cert refresh + autoupdate share a single commit when both fire in the same run | | Failure mode | Non-fatal: a failed npm view or npx emits ::warning:: and the build continues |

The shipped deploy.yml template already includes the required paths-ignore entries for both action directories. If you have an older deploy.yml checked in, add both entries to prevent the autoupdate commit-back from re-triggering the workflow.

deploy.yml is not auto-updated

deploy.yml is NOT auto-updated. GitHub's GITHUB_TOKEN cannot push changes to workflow files (.github/workflows/*.yml) regardless of contents: write — this is a built-in safeguard against CI self-modification. When a new version of gowalk-cicd requires deploy.yml schema changes (e.g., new permissions, new paths-ignore entries), the action's release notes will call this out and you must run npx --yes gowalk-cicd manually once to sync your deploy.yml. Existing deploy.yml stays untouched on every auto-update cycle until you do.

Opt out

Pin the vendored copy by passing auto-update: 'false' to the action:

- uses: ./.github/actions/swift-app
  with:
    auto-update: 'false'

First-run bootstrap

The marker is written by npx gowalk-cicd itself. A repo without a marker (e.g. an old hand-vendored copy) will be treated as out-of-date on its first run, after which updates land incrementally. Run npx --yes gowalk-cicd once locally if you want to skip even that first auto-bootstrap.

License

MIT