Skip to content
Draft
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 CHANGELOG/preview.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Preview Changelog

## [Unreleased]

### New Features
- New cmdlet `Reset-PSResourceRepository` which creates a fresh repository store by deleting the existing PSResourceRepository.xml file and registering only PSGallery with default settings

## [1.2.0-preview3](https://github.com/PowerShell/PSResourceGet/compare/v1.2.0-preview2..v1.2.0-preview3) - 2025-09-12

### New Features
Expand Down
1 change: 1 addition & 0 deletions src/Microsoft.PowerShell.PSResourceGet.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
'Get-PSScriptFileInfo',
'Install-PSResource',
'Register-PSResourceRepository',
'Reset-PSResourceRepository',
'Save-PSResource',
'Set-PSResourceRepository',
'New-PSScriptFileInfo',
Expand Down
51 changes: 50 additions & 1 deletion src/code/RepositorySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public static void CheckRepositoryStore()
}
catch (Exception e)
{
throw new PSInvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Repository store may be corrupted, file reading failed with error: {0}.", e.Message));
throw new PSInvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Repository store may be corrupted, file reading failed with error: {0}. Run 'Reset-PSResourceRepository' to reset the repository store to its default state.", e.Message));
}
}

Expand Down Expand Up @@ -845,6 +845,55 @@ public static List<PSRepositoryInfo> Read(string[] repoNames, out string[] error
return reposToReturn.ToList();
}

/// <summary>
/// Resets the repository store by deleting the existing file and creating a new one with PSGallery
/// Returns: PSRepositoryInfo for the newly registered PSGallery
/// </summary>
public static PSRepositoryInfo ResetRepositoryStore()
{
// Delete the existing repository file if it exists
if (File.Exists(FullRepositoryPath))
{
try
{
File.Delete(FullRepositoryPath);
}
catch (Exception e)
{
throw new PSInvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Failed to delete repository store file with error: {0}.", e.Message));
}
}

// Ensure directory exists
if (!Directory.Exists(RepositoryPath))
{
try
{
Directory.CreateDirectory(RepositoryPath);
}
catch (Exception e)
{
throw new PSInvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Failed to create repository directory with error: {0}.", e.Message));
}
}

// Create a new repository store file
try
{
XDocument newRepoXML = new XDocument(
new XElement("configuration"));
newRepoXML.Save(FullRepositoryPath);
}
catch (Exception e)
{
throw new PSInvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Repository store creation failed with error: {0}.", e.Message));
}

// Add PSGallery to the newly created store
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
return Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);
}

#endregion

#region Private methods
Expand Down
71 changes: 71 additions & 0 deletions src/code/ResetPSResourceRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.PowerShell.PSResourceGet.UtilClasses;
using System;
using System.Management.Automation;

namespace Microsoft.PowerShell.PSResourceGet.Cmdlets
{
/// <summary>
/// The Reset-PSResourceRepository cmdlet creates a fresh repository store by deleting
/// the existing PSResourceRepository.xml file and creating a new one with only PSGallery registered.
/// This is useful when the repository store becomes corrupted.
/// </summary>
[Cmdlet(VerbsCommon.Reset,
"PSResourceRepository",
SupportsShouldProcess = true,
ConfirmImpact = ConfirmImpact.High)]
[OutputType(typeof(PSRepositoryInfo))]
public sealed class ResetPSResourceRepository : PSCmdlet
{
#region Parameters

/// <summary>
/// When specified, displays the PSGallery repository that was registered.
/// </summary>
[Parameter]
public SwitchParameter PassThru { get; set; }

#endregion

#region Method overrides

protected override void ProcessRecord()
{
string repositoryStorePath = System.IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"PSResourceGet",
"PSResourceRepository.xml");

string actionMessage = $"Reset repository store at '{repositoryStorePath}'. This will delete all registered repositories and register only PSGallery.";

if (!ShouldProcess(repositoryStorePath, actionMessage))
{
return;
}

try
{
WriteVerbose("Resetting repository store");
PSRepositoryInfo psGallery = RepositorySettings.ResetRepositoryStore();
WriteVerbose($"Repository store has been reset. PSGallery registered at: {repositoryStorePath}");

if (PassThru)
{
WriteObject(psGallery);
}
}
catch (Exception e)
{
ThrowTerminatingError(new ErrorRecord(
new PSInvalidOperationException($"Failed to reset repository store: {e.Message}", e),
"ResetRepositoryStoreFailed",
ErrorCategory.InvalidOperation,
this));
}
}

#endregion
}
}
129 changes: 129 additions & 0 deletions test/ResourceRepositoryTests/ResetPSResourceRepository.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

$modPath = "$psscriptroot/../PSGetTestUtils.psm1"
Write-Verbose -Verbose -Message "PSGetTestUtils path: $modPath"
Import-Module $modPath -Force -Verbose

Describe "Test Reset-PSResourceRepository" -tags 'CI' {
BeforeEach {
$PSGalleryName = Get-PSGalleryName
$PSGalleryUri = Get-PSGalleryLocation
Get-NewPSResourceRepositoryFile
$tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1"
$tmpDir2Path = Join-Path -Path $TestDrive -ChildPath "tmpDir2"
$tmpDirPaths = @($tmpDir1Path, $tmpDir2Path)
Get-NewTestDirs($tmpDirPaths)
}
AfterEach {
Get-RevertPSResourceRepositoryFile
$tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1"
$tmpDir2Path = Join-Path -Path $TestDrive -ChildPath "tmpDir2"
$tmpDirPaths = @($tmpDir1Path, $tmpDir2Path)
Get-RemoveTestDirs($tmpDirPaths)
}

It "reset repository store with multiple repositories registered" {
# Register multiple repositories
Register-PSResourceRepository -Name "testRepository1" -Uri $tmpDir1Path
Register-PSResourceRepository -Name "testRepository2" -Uri $tmpDir2Path

# Verify multiple repositories exist
$repos = Get-PSResourceRepository
$repos.Count | Should -BeGreaterThan 2

# Reset repository store
Reset-PSResourceRepository -Confirm:$false

# Verify only PSGallery exists
$repos = Get-PSResourceRepository
$repos.Count | Should -Be 1
$repos.Name | Should -Be $PSGalleryName
$repos.Uri | Should -Be $PSGalleryUri
$repos.Priority | Should -Be 50
$repos.Trusted | Should -Be $false
}

It "reset repository store when only PSGallery is registered" {
# Reset repository store
Reset-PSResourceRepository -Confirm:$false

# Verify only PSGallery exists
$repos = Get-PSResourceRepository
$repos.Count | Should -Be 1
$repos.Name | Should -Be $PSGalleryName
}

It "reset repository store with -PassThru returns PSGallery" {
# Register a test repository
Register-PSResourceRepository -Name "testRepository1" -Uri $tmpDir1Path

# Reset repository store with PassThru
$result = Reset-PSResourceRepository -Confirm:$false -PassThru

# Verify PassThru returns PSGallery
$result.Name | Should -Be $PSGalleryName
$result.Uri | Should -Be $PSGalleryUri
$result.Priority | Should -Be 50
$result.Trusted | Should -Be $false

# Verify only PSGallery exists
$repos = Get-PSResourceRepository
$repos.Count | Should -Be 1
}

It "reset repository store respects -WhatIf" {
# Register test repositories
Register-PSResourceRepository -Name "testRepository1" -Uri $tmpDir1Path
Register-PSResourceRepository -Name "testRepository2" -Uri $tmpDir2Path

# Get count before reset
$reposBefore = Get-PSResourceRepository
$countBefore = $reposBefore.Count

# Reset with WhatIf
Reset-PSResourceRepository -WhatIf

# Verify repositories still exist (WhatIf should not make changes)
$reposAfter = Get-PSResourceRepository
$reposAfter.Count | Should -Be $countBefore
$reposAfter.Name | Should -Contain "testRepository1"
$reposAfter.Name | Should -Contain "testRepository2"
}

It "reset repository store when PSGallery was unregistered" {
# Unregister PSGallery
Unregister-PSResourceRepository -Name $PSGalleryName

# Verify PSGallery is not registered
$repos = Get-PSResourceRepository -ErrorAction SilentlyContinue
$repos.Name | Should -Not -Contain $PSGalleryName

# Reset repository store
Reset-PSResourceRepository -Confirm:$false

# Verify PSGallery is back
$repos = Get-PSResourceRepository
$repos.Count | Should -Be 1
$repos.Name | Should -Be $PSGalleryName
}

It "reset repository store with custom PSGallery settings" {
# Unregister default PSGallery and register with custom settings
Unregister-PSResourceRepository -Name $PSGalleryName
Register-PSResourceRepository -PSGallery -Priority 10 -Trusted

# Verify custom settings
$repo = Get-PSResourceRepository -Name $PSGalleryName
$repo.Priority | Should -Be 10
$repo.Trusted | Should -Be $true

# Reset repository store
Reset-PSResourceRepository -Confirm:$false

# Verify PSGallery is reset to default settings
$repo = Get-PSResourceRepository -Name $PSGalleryName
$repo.Priority | Should -Be 50
$repo.Trusted | Should -Be $false
}
}