Skip to content

feat: Implement azdo pipelines variable create command #128

@tmeckel

Description

@tmeckel

This issue tracks the implementation of the azdo pipelines variable create command.

Command Description

Create a variable on a classic pipeline (build definition). The Azure CLI locates the pipeline by ID or name, validates duplicate keys, and updates the definition via BuildClient.update_definition (source). azdo should replicate this workflow with consistent prompting, masking, and output.

Important REST constraint:

  • Azure DevOps REST never returns secret variable values. The command must never attempt to print secret values and must not rely on the server returning them after creation.

azdo Command Signature

azdo pipelines variable create [ORGANIZATION/]PROJECT/PIPELINE_ID_OR_NAME --name VARIABLE_NAME [flags]

Flags:

  • --value: Initial value. Optional; when omitted and --secret is false, default to an empty string.
  • --secret: Mark the variable as secret; if --value is omitted, read from AZDO_PIPELINES_VAR_ or prompt securely.
  • --allow-override: Boolean controlling whether the variable can be overridden at queue time.
  • JSON export flags.

Behavior

  • Parse [ORGANIZATION/]PROJECT/PIPELINE_ID_OR_NAME using util.ParseProjectTargetWithDefaultOrganization.
  • Resolve project scope, determine the pipeline ID by querying build definitions when only a name is provided.
  • Start the progress indicator.
  • Fetch the pipeline definition; reject duplicates by checking definition.Variables case-insensitively.
  • Compute the variable value (handle secret prompts/environment) and populate a BuildDefinitionVariable model.
  • Call UpdateDefinition and extract the stored variable from the response (respecting case changes applied by Azure DevOps).
  • Stop progress before printing output.
  • Output:
    • Normal output: render the created variable as a single-object Go text template.
    • If the variable is secret, display *** instead of a value.
  • JSON export:
    • Do not invent a “view struct” by default.
    • Exception (allowed): because the SDK variable model does not include the variable name as a field (it is typically the map key), emit an augmented item that embeds the SDK variable model and adds the name.
    • Ensure secret values are redacted/omitted (and do not depend on the server returning them).
  • Add debug logging for pipeline resolution, allow-override state, and prompt source (without exposing secrets).

Command Wiring

  • Implement the command in internal/cmd/pipelines/variable/create/create.go with NewCmd(ctx util.CmdContext) *cobra.Command.
  • Register it from internal/cmd/pipelines/variable/variable.go using cmd.AddCommand(create.NewCmd(ctx)) so it appears under azdo pipelines variable.

Implementation Notes (filled checklist)

  • Implement command: internal/cmd/pipelines/variable/create/create.go (type opts struct, NewCmd(ctx), run(ctx, opts)).
  • Wire command: internal/cmd/pipelines/variable/variable.go must AddCommand(create.NewCmd(ctx)) and be reachable from azdo pipelines.
  • Parse scope: util.ParseProjectTargetWithDefaultOrganization(ctx, targetArg) for [ORGANIZATION/]PROJECT/PIPELINE_ID_OR_NAME; wrap parse errors with util.FlagErrorWrap.
  • Client: Build via ctx.ClientFactory().Build(ctx.Context(), scope.Organization).
  • Resolve pipeline:
    • If PIPELINE_ID_OR_NAME is numeric, treat as definitionId.
    • Otherwise resolve by name via build definitions list (handle multiple matches explicitly).
  • Create algorithm:
    • GET definition by id.
    • Validate variable key does not already exist (case-insensitive).
    • Determine value source: --value or secure prompt when secret; never log the value.
    • Add variable entry to definition.Variables.
    • PUT updated definition via UpdateDefinition, ensuring revision matches the GET result and unrelated definition fields are preserved.
  • Output:
    • Normal output: single-object Go text template for the created variable; never print secret values (use ***).
    • JSON output: emit SDK model (or augmented model if adding the variable name), with secret value redaction/omission.
  • Tests:
    • Add unit tests at internal/cmd/pipelines/variable/create/create_test.go.
    • Hermetic mocks: Build client + prompter (only if prompt path is used).
    • Table-driven cases: creation by id, lookup by name (including collision handling), secret prompt flow, duplicate variable rejection, JSON mode redaction.

SDK / Client Requirements

  • Requires the Build client (ClientFactory().Build(...)) to fetch and update pipeline definitions. Confirm the client is exposed; if not, follow "Handling Missing Azure DevOps SDK Clients" in AGENTS.md.

Testing

  • Unit tests covering:
    1. Creation on pipeline ID.
    2. Lookup by pipeline name.
    3. Secret variable prompt flow.
    4. Duplicate variable rejection.
    5. JSON export path.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions