Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<b>The official Infisical CLI</b>: Inject secrets into applications and manage your Infisical infrastructure.
</p>

> [!IMPORTANT]
Comment thread
victorvhs017 marked this conversation as resolved.
> **The Infisical CLI Linux package repository is moving off Cloudsmith.** To keep up with download volume, we're migrating the Linux package repository to our own host at `artifacts-cli.infisical.com`. Cloudsmith downloads will stop being served on **September 16th, 2026**, after which installs and updates from the old URL will fail.
>
> Every release, including all older versions, is already available on the new host. If you're on an existing setup, you don't need to change anything else, just repoint your machine to the new artifact URL by following the [migration steps](https://infisical.com/docs/cli/cloudsmith-migration).

## Introduction

The **[Infisical CLI](https://infisical.com/docs/cli/overview)** is a powerful command-line tool for secret management that allows you to:
Expand Down
2 changes: 1 addition & 1 deletion packages/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ func init() {

config.INFISICAL_URL = util.AppendAPIEndpoint(resolveDomain(cmd, config.INFISICAL_URL))

// util.DisplayAptInstallationChangeBannerWithWriter(silent, cmd.ErrOrStderr())
if !util.IsRunningInDocker() && !silent && !isStructuredOutputRequested(cmd) {
util.CheckForUpdateWithWriter(cmd.ErrOrStderr())
util.DisplayPackageRepoMigrationNoticeWithWriter(silent, cmd.ErrOrStderr())
}

loggedInDetails, err := util.GetCurrentLoggedInUserDetails(false)
Expand Down
88 changes: 74 additions & 14 deletions packages/util/check-for-update.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,27 +209,87 @@ func writeUpdateCheckCache(cache *UpdateCheckCache) error {
return nil
}

func DisplayAptInstallationChangeBanner(isSilent bool) {
DisplayAptInstallationChangeBannerWithWriter(isSilent, os.Stderr)
const migrationNoticeCacheTTL = 24 * time.Hour

const migrationGuideURL = "https://infisical.com/docs/cli/cloudsmith-migration"

const migrationCloudsmithSunset = "September 16, 2026"

type migrationNoticeCache struct {
LastShownTime time.Time `json:"lastShownTime"`
}

func DisplayAptInstallationChangeBannerWithWriter(isSilent bool, w io.Writer) {
if isSilent {
func getMigrationNoticeCachePath() (string, error) {
homeDir, err := GetHomeDir()
if err != nil {
return "", err
}
return filepath.Join(homeDir, CONFIG_FOLDER_NAME, MIGRATION_NOTICE_CACHE_FILE_NAME), nil
}

// migrationNoticeRecentlyShown returns true if the notice was shown within the TTL.
func migrationNoticeRecentlyShown() bool {
path, err := getMigrationNoticeCachePath()
if err != nil {
return false
}
data, err := os.ReadFile(path)
if err != nil {
return false
}
var cache migrationNoticeCache
if err := json.Unmarshal(data, &cache); err != nil {
return false
}
return time.Since(cache.LastShownTime) < migrationNoticeCacheTTL
}

// recordMigrationNoticeShown stamps the cache so the notice is throttled.
func recordMigrationNoticeShown() {
path, err := getMigrationNoticeCachePath()
if err != nil {
return
}
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return
}
data, err := json.Marshal(migrationNoticeCache{LastShownTime: time.Now()})
if err != nil {
return
}
// Best-effort write; a failure just means the notice may show again next run.
_ = os.WriteFile(path, data, 0600)
}

if runtime.GOOS == "linux" {
_, err := exec.LookPath("apt-get")
isApt := err == nil
if isApt {
yellow := color.New(color.FgYellow).SprintFunc()
msg := fmt.Sprintf("%s",
yellow("Update Required: Your current package installation script is outdated and will no longer receive updates.\nPlease update to the new installation script which can be found here https://infisical.com/docs/cli/overview#installation debian section\n"),
)
func DisplayPackageRepoMigrationNotice(isSilent bool) {
DisplayPackageRepoMigrationNoticeWithWriter(isSilent, os.Stderr)
}

fmt.Fprintln(w, msg)
}
// DisplayPackageRepoMigrationNoticeWithWriter prints a one-time-per-day notice
// that the Linux package repository has moved off Cloudsmith.
// Stays quiet in --silent mode, and can be disabled with INFISICAL_DISABLE_MIGRATION_NOTICE.
func DisplayPackageRepoMigrationNoticeWithWriter(isSilent bool, w io.Writer) {
if isSilent {
return
}
if os.Getenv("INFISICAL_DISABLE_MIGRATION_NOTICE") != "" {
return
}
if migrationNoticeRecentlyShown() {
return
}

yellow := color.New(color.FgYellow).SprintFunc()
bold := color.New(color.FgYellow, color.Bold).SprintFunc()
fmt.Fprintln(w, bold("Important: the Infisical CLI Linux package repository is moving off Cloudsmith."))
fmt.Fprintln(w, yellow(
"What's happening: Cloudsmith stops serving on "+migrationCloudsmithSunset+". After that, installing or\n"+
"updating the CLI on Linux from the old Cloudsmith URL (apt, yum/dnf, apk) will fail.\n"+
"What to do: repoint your machine to the new host (artifacts-cli.infisical.com).\n"+
"Migration steps: "+migrationGuideURL+"\n",
))

recordMigrationNoticeShown()
}

func getLatestTag(repoOwner string, repoName string) (string, time.Time, bool, error) {
Expand Down
3 changes: 2 additions & 1 deletion packages/util/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ const (
KUBERNETES_SERVICE_ACCOUNT_CA_CERT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token"

UPDATE_CHECK_CACHE_FILE_NAME = "update-check.json"
UPDATE_CHECK_CACHE_FILE_NAME = "update-check.json"
MIGRATION_NOTICE_CACHE_FILE_NAME = "migration-notice.json"
)

var (
Expand Down
15 changes: 14 additions & 1 deletion upload_to_s3.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,20 @@ if ls *.apk 1> /dev/null 2>&1; then
# Sync existing packages from S3 (to preserve old versions)
echo "Syncing existing APK packages from S3..."
aws s3 sync "s3://$INFISICAL_CLI_S3_BUCKET/apk/" apk-staging/ --exclude "*/APKINDEX.tar.gz"


# Integrity gate: the APKINDEX is rebuilt from staging, so a partial sync would
# silently drop versions. Staging = synced repo + new apks, so it must have at
# least as many .apk as S3 per arch; if fewer, the sync was incomplete, so abort.
for arch in x86_64 aarch64; do
s3_count=$(aws s3 ls "s3://$INFISICAL_CLI_S3_BUCKET/apk/stable/main/$arch/" 2>/dev/null | grep -c '\.apk$' || true)
local_count=$(ls apk-staging/stable/main/$arch/*.apk 2>/dev/null | wc -l | tr -d ' ')
if [ "$local_count" -lt "$s3_count" ]; then
echo "Error: APK sync incomplete for $arch (S3 has $s3_count, staged $local_count). Aborting to avoid publishing a stale APKINDEX."
exit 1
fi
echo "APK integrity OK for $arch: staged $local_count >= S3 $s3_count"
done

# Validate APK private key exists
if [ ! -f "$APK_PRIVATE_KEY_PATH" ]; then
echo "Error: APK private key not found at $APK_PRIVATE_KEY_PATH"
Expand Down
Loading