Module: GitHub#28
Merged
Merged
Conversation
Initial scaffolding for a new `Modules.GitHub.Actions` module that will expose the full GitHub REST API as PAD actions, generated at build time from the live `github/rest-api-description` OpenAPI document. This commit contains the buildable skeleton only; subsequent commits in the same PR will add the T4 templates (Microsoft.OpenApi parser used build-time-only via `PrivateAssets="all" ExcludeAssets="runtime"`), the `Login` action and its three `ActionSelector`s, and the auto-generated action/POCO/resx layer. Solution-file cleanup is included so the new project is registered: * `PowerAutomate.Desktop.sln` deleted; only `PowerAutomate.Desktop.slnx` is maintained. * CI / PR / nightly workflows switched from `.sln` to `.slnx`. Tooling: * `.tools/update-github-openapi.ps1` fetches the OpenAPI spec into `modules/Modules.GitHub.Actions/openapi.json`. * The spec file is git-ignored; the tools script is the supported way to refresh it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds the `Login` action and its three `ActionSelector`s that mirror the `gh auth login` shape, plus the `GitHubAuthenticationContext` custom type that subsequent generated REST actions will consume: * `LoginAction` (id `Login`) with input groups General / PersonalAccessToken / DeviceFlow / GitHubCli and a `Mode` enum that drives which branch executes. * `LoginWithPersonalAccessTokenActionSelector` accepts a PAT directly. * `LoginWithDeviceFlowActionSelector` runs the OAuth device flow against the publicly-known GitHub CLI client id (configurable), copies the user code to the clipboard, opens the verification URL in the default browser, then polls until a token is issued or the device code expires. * `LoginWithGitHubCliActionSelector` shells out to `gh auth token --hostname <Host>` to reuse a token already stored locally. * All three modes funnel into `CreateAuthenticatedContext` which builds a long-lived `HttpClient` configured with `Authorization: Bearer <token>`, `User-Agent` and `Accept: application/vnd.github+json`, then best-effort resolves the authenticated login by GETting `/user`. `GitHubAuthenticationContext` is marked `[Type]` so PAD recognises it as a custom variable type. The `HttpClient` is hidden from the variable picker (`[PropertyIgnore]`); `BaseUrl` / `UserAgent` / `Login` are exposed. `Modules.GitHub.Actions` is wired into `tests/Modules.Actions.Tests` so the existing `ActionTests`/`ActionSelectorTests` fixtures auto-validate the new module. Full test suite (8/8) green; bin/ contains only the SDK, Newtonsoft, and the module assembly - no generator artifacts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds the `Models.tt` T4 template that emits clean Newtonsoft-annotated POCO classes for every schema in `components.schemas` of GitHub's OpenAPI document, into `PowerAutomate.Desktop.Modules.GitHub.Actions.Models`. * 936 schemas in the live spec -> 908 `partial class` POCOs and 28 string `enum`s in the committed `Models.cs` (~780 KB). * `Microsoft.OpenApi` and `Microsoft.OpenApi.Readers` 1.6.29 are referenced with `PrivateAssets="all" ExcludeAssets="runtime"` and `GeneratePathProperty="true"` so the parser is design-time only and never reaches the module's `bin/` or the CAB. * Inline / complex shapes (`oneOf`, `anyOf`, `allOf` of inline schemas, inline objects without `properties`) collapse to `JObject` so the generated code stays compilable; this is the documented fidelity loss agreed in the plan. * `allOf` composition is flattened to a single class. * C# keyword and digit-leading property/enum-member names are sanitised. Regeneration is via `.tools/regenerate-github-module.ps1` (matches the existing `.tools/` script pattern): it substitutes `%NUGET_PACKAGES%` in the templates with the local NuGet cache path and invokes `dotnet-t4` once per `*.tt`. Generated outputs are committed; CI does not regenerate. Module assembly grew from 26 KB to 1.27 MB; all 8 existing tests still pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds the `Actions.tt` and `Resources.tt` T4 templates that close the loop
on the generator pipeline. They walk every operation in `components.paths`
of the GitHub OpenAPI document and emit:
* `Actions.cs` (3.0 MB committed): 1190 `ActionBase` subclasses, one per
REST operation. Each:
- Carries `[Action(Id=..., Category=<Tag>)]` so the PAD designer groups
them by GitHub tag (Repos, Pulls, Issues, Actions, ...).
- Declares `[Group]` attributes for `Authentication` / `Path` /
`Query` / `Header` / `Body` as actually used by the operation.
- Takes `GitHubAuthenticationContext Authentication` as its first required
input (driven by the `Login` action shipped previously).
- Builds an `HttpRequestMessage` against the auth context's long-lived
`HttpClient`, interpolates path parameters, appends query parameters,
serializes JSON bodies with Newtonsoft.Json, and deserializes responses
into the POCO `Models` (or `JObject` when the response is too dynamic
to model statically).
- Wraps everything in try/catch -> `ActionException(ErrorCodes.Unknown,...)`.
* `Properties/Resources.resx` (~3.0 MB committed): full localisable resource
set (default `en` only) covering every generated action / argument /
group / error / enum value plus the hand-written module + Login entries -
satisfies every assertion in the existing `ActionTests` and
`ActionSelectorTests` fixtures.
Other plumbing:
* `Modules.GitHub.Actions.csproj` registers `Actions.tt` / `Actions.cs`
alongside `Models.tt` / `Models.cs` (mirrors the PetStore wiring).
* `.tools/regenerate-github-module.ps1` now also routes `Resources.tt` to
`Properties\Resources.resx`.
Verified locally:
- `dotnet build` succeeds with 7 warnings (6 are CS0472 on enum-typed query
parameters because top-level OpenAPI enums currently surface as `string`
with a non-nullable comparison; functional but cosmetic; pre-existing
PetStore obsolete warning is the seventh).
- All 8 tests pass.
- `Modules.GitHub.Actions.dll` is 5.70 MB and the CAB is 1.05 MB.
- bin/Debug contains only the SDK, Newtonsoft, and the module assembly;
no `Microsoft.OpenApi.*` / `SharpYaml` / Kiota / NSwag artifacts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When MSBuild's `Exec` task runs `powershell -ExecutionPolicy Bypass -File sign.ps1` through `cmd.exe`, Windows PowerShell 5.1 starts in an environment where PowerShell 7's module directory sits ahead of 5.1's in `PSModulePath`. As a result PS5.1 loads PS7's `Microsoft.PowerShell.Security` module, which does not register the `Certificate` provider - so the `Get-ChildItem -Path Cert:\CurrentUser\My` call in `sign.ps1` fails with "A drive with the name 'Cert' does not exist" and `sign` is then invoked with no `--certificate-fingerprint` argument, leaving the DLL and CAB unsigned. PAD then rejects the module with "The desktop flow module package is not correctly signed." Switch to the `System.Security.Cryptography.X509Certificates.X509Store` API, which does not depend on the `Cert:` PSDrive being registered, and at the same time: * Filter out expired certificates (the previous behaviour would silently sign with the first `CN=` match even after the self-signed cert from `setup.ps1` had expired). * Prefer the newest non-expired cert when more than one matches. * Throw with a clear remediation message when no usable cert exists, and propagate the `sign` tool's exit code so the MSBuild target actually fails on a signing failure instead of silently producing an unsigned CAB. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PAD's module loader rejected the module with several errors:
1. `Enum Argument '<X>' in action '<...>' must have a default value.`
2. `Argument '<X>' of action '<...>' is defined as not required but its type is not nullable`
3. `Invalid argument name 'Label' in action 'ReposUploadReleaseAsset'`
Updates `Actions.tt` and `Resources.tt` accordingly:
* For every parameter whose schema is a `` to a top-level string-enum
schema in `components.schemas`, the generated property now:
- Resolves to `Models.<Enum>?` (optional) or `Models.<Enum>`
(required) instead of `string` so PAD recognises it as an enum.
- Carries `[System.ComponentModel.DefaultValue(null)]` for optional
enums and `[System.ComponentModel.DefaultValue(Models.<Enum>.<First>)]`
for required ones.
- Has a query-string emitter that skips the value when `HasValue` is
false (and emits without a redundant `!= null` check for non-nullable
enums, which also clears the 6 pre-existing CS0472 warnings).
* Adds the **full Robin keyword set** (extracted from
`Microsoft.Flow.RPA.Desktop.Robin.Language.Parsing.LanguageLexer`) to a
`PadReservedPropertyNames` set used by `ToPropertyName`. Any
parameter whose PascalCase name case-insensitively collides with a Robin
keyword (`Label`, `Input`, `Output`, `Action`, `For`, `If`,
`True`, etc.) is suffixed with `Argument` (so the previous `Label`
on `ReposUploadReleaseAsset` becomes `LabelArgument`). The same set
is added to `Resources.tt` so the resx keys match the new property
names and the existing `ActionTests` continue to pass.
Verified locally: full build clean (0 warnings, 0 errors, down from 7),
8/8 tests pass, the generated CAB is Authenticode-valid, and bin/Debug
still contains only the module assembly + SDK + Newtonsoft (no generator
artefacts).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds three test methods to `tests/Modules.Actions.Tests/ActionTests.cs` so the same class of failures that PAD reported on module install is now caught by `dotnet test` against any module under `modules/` - including ones produced by future T4 generators. * `Action_All_InputArguments_All_Enums_HaveDefaultValue` - every input argument whose CLR type (or underlying type, for `Nullable<TEnum>`) is an enum must carry `[System.ComponentModel.DefaultValue(...)]`. * `Action_All_InputArguments_All_NonRequired_AreNullableOrHaveDefaultValue` - every input argument with `[InputArgument(Required = false)]` must either have a nullable CLR type (reference type or `Nullable<T>`) or a `[DefaultValue]` attribute (matches what the PAD module loader actually accepts; the long-standing `Modules.GitHub.Copilot.Actions` pattern of non-nullable enum + `DefaultValue(EnumName.Unset)` is preserved). * `Action_All_Arguments_All_HaveNonReservedNames` - no input or output argument name may collide (case-insensitively) with a Robin language keyword. The reserved-name set is the same 45-keyword list extracted from `Microsoft.Flow.RPA.Desktop.Robin.Language.Parsing.LanguageLexer` and used by `Actions.tt` / `Resources.tt`. Each test follows the existing fixture style: `ModuleEnumerator.GetAllAssemblies()` + LINQ over `[Action]` types, `[InputArgument]` / `[OutputArgument]` properties, with `Assert.That(..., message)` failure messages that mirror PAD's own wording so the failure is immediately recognisable. All 11 tests pass against the current set of referenced modules (8 existing + 3 new); build clean (only the pre-existing PetStore obsolete warning). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Switches the lossy `oneOf` / `anyOf` / inline-object fallback type
in `Actions.tt` and `Models.tt` from `JObject` to `JToken`. Some
GitHub endpoints (notably `GET /repos/{owner}/{repo}/stargazers`)
legitimately return a JSON array instead of an object, and
Newtonsoft.Json refuses to deserialize a JArray into a JObject. `JToken`
is the common ancestor and accepts both shapes; consumers cast as needed.
* Adds `Console.WriteLine` output in `LoginWithDeviceFlow` so the
verification URL and user code are visible to CLI consumers (PAD users
still get the clipboard copy + browser open). Purely additive.
* New `.tools\smoke-test-github-module.ps1`: loads the module assembly,
signs in via the local `gh` CLI (or, when the device-flow client id
is updated, via OAuth device flow), then exercises 56 representative
REST actions across every code-generation pattern (no-param GETs,
path-param GETs, multi-path GETs, enum query params, oneOf-heavy
responses such as `ReposGetRepoRulesets` and `ReposGetBranchRules`,
search endpoints, special accept headers, PUT/GET/DELETE side-effect
chains) plus a five-step gist mutation chain
(create -> get -> update -> delete -> verify-gone) that proves
POST/PATCH/DELETE with `JToken` body and POCO output deserialization
(`Models.GistSimple`) all work end-to-end against the live GitHub
REST API. Every test passed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Significantly expands `.tools/smoke-test-github-module.ps1` to exercise patterns that the original ~50-action pass didn't reach: * **Array query parameters** (`IssuesListForRepo` with `Labels='bug,enhancement'`) * **DateTime query parameters** (`Since=2025-01-01T00:00:00Z`) * **Explicit pagination** (`PerPage=2 Page=1` against `ReposListForAuthenticatedUser`) * **404 propagation** for missing repo + missing user * **MS-connector parity operations** that the Power Platform GitHub connector docs page enumerates: `IssuesGet`, `IssuesListLabelsOnIssue`, `IssuesListAssignees`, `ReposListCollaborators`, `GitGetRef`, `ReposCompareCommits`. * **Output assertions** (10/10) that verify roundtripped data, not just status: `UsersGetAuthenticated.login` / `.type` via `JToken` indexer access (the response is a `oneOf` of public/private user, so the generator picks `JToken`); `ReposGet.Name` / `.FullName`; `ReposListBranches` count and `main` membership; `PullsGet.Number/.State`; `RateLimit.Rate.Limit`; pagination cap. * **Full issue lifecycle** (7 steps) - create, get, add comment, list comments, update title + close, reopen, close again - proves `POST/PATCH` with `JToken` body + POCO output (`Models.Issue`) end-to-end against the live API. Roundtrip Title equality verified. * **Step numbering** in the script tidied (Steps 1-6). Total: 80 live GitHub API operations exercised, every one behaves as expected. The `JToken` shift (committed earlier) lets all `oneOf` responses through cleanly. Note: two auto-generated test issues (#29, #30) were created in jfevia/PowerAutomate.Desktop.Modules and immediately closed; the GitHub REST API does not allow deleting closed issues without admin UI action. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Status: Draft / work-in-progress. Initial scaffold only.
Goal
Add a new
Modules.GitHub.Actionsmodule that exposes the full GitHub REST API as Power Automate Desktop actions, generated at build time from the livegithub/rest-api-descriptionOpenAPI document. The module lives alongsideModules.GitHub.Copilot.Actionsand follows the same packaging conventions as every other module undermodules/.What is in this commit
modules/Modules.GitHub.Actions/skeleton (csproj,ErrorCodes.cs,Properties/Resources.{resx,Designer.cs}).Microsoft.OpenApi 3.6.0added toDirectory.Packages.props. The module references it withPrivateAssets="all" ExcludeAssets="runtime"andGeneratePathProperty="true", so the parser is available to T4 templates at build time but is never copied to the module'sbin/and never ends up in the CAB..tools/update-github-openapi.ps1fetches the spec intomodules/Modules.GitHub.Actions/openapi.json(git-ignored).PowerAutomate.Desktop.slndeleted; onlyPowerAutomate.Desktop.slnxis kept. CI / PR / nightly workflows already switched to.slnx./Modules/folder in.slnx.Verified locally:
No
Microsoft.OpenApi.*(or any other generator) DLL lands inbin/. Existing test suite (8/8) passes.What is coming in this PR (subsequent commits)
Loginaction + threeActionSelectors (LoginWithPersonalAccessToken,LoginWithDeviceFlow,LoginWithGitHubCli), producing a customGitHubAuthenticationContext(long-livedHttpClient, configurableBaseUrl/UserAgent).Models.tt— emits clean Newtonsoft-annotated POCOs by walkingcomponents.schemaswithMicrosoft.OpenApi.Actions.tt— emits oneActionBasesubclass per OpenAPI operation, each invokingHttpClientdirectly (no generator runtime).Resources.tt— emits the en-onlyResources.resxcovering all action / argument / error / group / enum names required by the existingActionTests.Modules.GitHub.Actionsintotests/Modules.Actions.Tests/Modules.Actions.Tests.csproj..cabcontains zero generator artifacts.README.md.Design context (for reviewers)
Loginaction with aLoginModeenum + threeActionSelectors; PAT, OAuth device flow (defaults to gh CLI's public client id), and reading from localgh auth token --hostname <Host>. Every generated REST action takes the resultingGitHubAuthenticationContextas its first required input.Microsoft.OpenApirather than NSwag/Kiota. NSwag chokes on a handful ofoneOfschemas in GitHub's spec (repository-rule,repository-ruleset-conditions, secret-scanningfirst_location_detected) — emits references without declarations and there is no NSwag option that fixes this. Kiota handlesoneOfcorrectly but would leak itsMicrosoft.Kiota.*abstractions into the runtime module unless we wrote a Kiota-reflecting transpiler in the T4 — at which point it is simpler to just parse the spec ourselves withMicrosoft.OpenApi.NET, a pure parser library with no runtime types.PrivateAssets="all" ExcludeAssets="runtime"the parser is build-only, the actions module references only the SDK +Newtonsoft.Json+System.Net.Http, and the CAB stays clean.Deferred follow-ups (not in this PR)
new HttpClient()-per-call port-exhaustion risk inModules.CloudFlows.Actions/RunFlowAction,samples/Modules.PetStore.Actions/Actions.tt-generatedActions.cs, etc. The pattern landed in this module (GitHubAuthenticationContextowns a long-livedHttpClient) is the suggested fix.