Cut GitHub API dependency and fix silent-failure paths - #2
Merged
Merged
Conversation
Reduces the unauthenticated api.github.com dependency and fixes a set of
defects that made failures look like successes.
GitHub API rate limiting
Get-GitHubInstaller made two api.github.com calls per install with no
credentials. That allowance is 60 requests/hour per source IP, so a site
behind a single NAT egress gets roughly 30 installs an hour before every
subsequent install fails at the resolve step. Three layers now sit in
front of it:
- resolved manifests are cached to ProgramData\TecharyGet\ManifestCache,
so repeat and retry installs of the same app cost no API calls
- on a live failure the cache is reused even when stale, so a rate
limited or offline site keeps installing from last known good instead
of hard failing
- an optional PAT (-GitHubToken, or TECHARYGET_GITHUB_TOKEN) lifts the
allowance to 5000/hour, and a rate limit response now says so
The scraping logic moves to Private/Resolve-GitHubManifest.ps1 so
resolution and download are separable and the cache has a seam to sit on.
Custom catalog was never reachable
Get-CustomApp pointed at .../BETA/TecharyGet/Private/CustomApps.json.
There is no TecharyGet directory in this repo, so that URL 404s on every
call and the catalog never synced. Corrected to the real path. The
download is also staged and parsed before it replaces the cache, so a
captive portal or proxy page returning HTTP 200 with HTML can no longer
poison the cache for an hour.
Failures that reported as success
- Install-TecharyApp returned silently for an unknown Id. A caller
driving this from Intune or an RMM cannot tell that from a successful
install, so a mistyped Id was reported as success. It now throws.
- Write-PackagerLog called EventLog::SourceExists unguarded. That throws
SecurityException when the caller cannot read the event log registry,
which is the normal non-elevated case, so every log call became a
terminating error. Event logging is now best effort and the file log
is likewise non-fatal.
- Uninstall-TecharyApp left $Arguments unset when an MSI uninstall
string contained no product code, reaching Start-Process as $null.
Both variables are initialised and that case now reports the reason.
- Uninstall-TecharyApp appended /S /silent /quiet /norestart to
QuietUninstallString, which is silent by definition. Passing
contradictory flags made some vendors' uninstallers fail or fall back
to a UI prompt. The switches are only appended to a plain
UninstallString now.
Version selection
Manifest versions were cast to [Version] and the failures discarded, which
silently dropped real releases such as 1.2.3-beta and 20240101, and threw
outright when no folder happened to parse. Replaced with a zero padded
sortable key over fixed width components, which also fixes 1.2 sorting
above 1.2.3, and prefers a stable release over a prerelease on a tie.
Module loading and manifest
The loader used $MyInvocation.MyCommand.Path, errored on a missing
directory and reported a failed dot-source as a later "command not found".
It now uses $PSScriptRoot, skips absent directories and fails immediately
naming the file and the reason. FunctionsToExport advertised
Show-IntunePackager, which has no implementation; removed. Version 2.4.
Verified: all files parse, Test-ModuleManifest passes, the module imports
and exports 8 commands, live resolution succeeds for 7zip.7zip,
Notepad++.Notepad++ and Google.Chrome, and version ordering is asserted
across mixed release, date and prerelease formats.
This was referenced Sep 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Get-GitHubInstallermakes two unauthenticatedapi.github.comcalls per install, against an allowance of 60 requests/hour per source IP. A customer site behind a single NAT egress gets ~30 installs an hour before every subsequent install fails, and it fails at the resolve step, so no package is produced at all. Measured while testing: resolving three packages took this IP from 59 to 53.Separately, several failures reported as successes. The worst is
Install-TecharyAppreturning silently for an unknownId— a caller driving this from Intune or an RMM cannot distinguish that from a successful install.Reducing the API dependency
ProgramData\TecharyGet\ManifestCache-GitHubToken/TECHARYGET_GITHUB_TOKEN)The scraping logic moves to
Private/Resolve-GitHubManifest.ps1so resolution and download are separable and the cache has a seam to sit on. Behaviour and the returned object are unchanged.The custom catalogue was never reachable
Get-CustomApppointed at.../BETA/TecharyGet/Private/CustomApps.json. There is noTecharyGet/directory in this repo, so that URL 404s on every call and the catalogue has never synced — it has been running off the module-local copy. Verified against live GitHub:The download is now staged and parsed before it replaces the cache, so a proxy or captive-portal page returning HTTP 200 with HTML cannot poison the cache for an hour.
Failures that reported as success
Install-TecharyAppreturned silently on unknownId→ now throws.Write-PackagerLogcalledEventLog::SourceExistsunguarded. That throwsSecurityExceptionwhen the caller cannot read the event-log registry — the normal non-elevated case — turning every log call into a terminating error. Event logging is now best-effort; the file log is likewise non-fatal.Uninstall-TecharyAppleft$Argumentsunset when an MSI uninstall string had no product code, reachingStart-Processas$null. Both variables are initialised and that case now reports the reason.Uninstall-TecharyAppappended/S /silent /quiet /norestarttoQuietUninstallString, which is silent by definition. Contradictory flags make some uninstallers fail or fall back to a UI prompt, which hangs indefinitely under SYSTEM. Those switches are now only appended to a plainUninstallString.Version selection
Manifest versions were cast to
[Version]with failures discarded, dropping real releases like1.2.3-betaand20240101, and throwing outright when no folder parsed. Replaced with a zero-padded sortable key over fixed-width components. This also fixes1.2sorting above1.2.3, and prefers a stable release over a prerelease on a tie:Module loading and manifest
The loader used
$MyInvocation.MyCommand.Path, errored on a missing directory, and surfaced a failed dot-source as a later "command not found". It now uses$PSScriptRoot, skips absent directories, and fails immediately naming the file and the reason.FunctionsToExportadvertisedShow-IntunePackager, which has no implementation — removed. Version 2.4.Verification
Test-ModuleManifestpasses; module imports and exports 8 commands7zip.7zip(26.03),Notepad++.Notepad++(8.9.8),Google.Chrome(153.0.8010.53)Related
Defects found in adjacent code during this work are handled separately: #4 (MSIX removal under SYSTEM), #5 (sideloading policy left enabled), #6 (detection precision).