Files
Gokul 09903569eb feat(evaluator): make toJSON(needs) return nested needs object (#98)
* feat(evaluator): make toJSON(needs) return nested needs object

Same bug that toJSON(steps) had before #97, and toJSON(env) had
before #96: resolve() had no match arm for the bare "needs"
identifier, so it fell through to Null. Anyone trying to dump the
cross-job outputs context got "null" back. Useful.

Build the shape GHA actually provides: each job ID maps to an
object with "outputs" (sub-object) and "result". Merge the key
sets of needs_context and needs_results so a job appears even if
only one of the two has recorded something for it — same
union-of-keys trick as the steps arm.

Omit "result" when no result was recorded, mirroring how steps
omits outcome/conclusion in that case. The only real shape
difference vs. steps is the single "result" string instead of the
(outcome, conclusion) pair.

Tests mirror the toJSON(steps) suite: populated context, empty,
sorted keys, one-sided populations, and special-character
escaping. Also drops "needs" from the lingering TODO comment —
github, secrets, matrix still to go.

* chore(secrets): silence clippy::unnecessary_sort_by in masker

Clippy 1.95 decided that `sort_by(|a, b| b.0.len().cmp(&a.0.len()))`
is an unnecessary dance when `sort_by_key` with `Reverse` says the
same thing in fewer tokens. CI is now `-D warnings`, so this was
the difference between a green build and a red one.

Swap to `sort_by_key(|pair| std::cmp::Reverse(pair.0.len()))`.
Same descending-by-length order, same behavior, clippy goes quiet.
Not a change anyone will miss.
2026-04-18 19:03:41 +05:30
..
2025-09-05 08:22:15 +05:30

wrkflw-secrets

Secrets management for wrkflw workflow execution. Provides secure handling of secrets with multiple providers, encryption, masking, and GitHub Actions-compatible ${{ secrets.* }} substitution.

Features

  • Providers: environment variables, files (JSON/YAML/.env), HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, GCP Secret Manager
  • Encryption: AES-256-GCM encrypted storage for secrets at rest
  • Masking: automatic masking of secrets in logs (GitHub tokens, AWS keys, JWTs, etc.)
  • Substitution: GitHub Actions-compatible ${{ secrets.* }} and ${{ secrets.provider:name }} syntax
  • Caching: optional TTL-based cache for frequently accessed secrets
  • Rate limiting: built-in protection against secret access abuse
  • Validation: comprehensive input validation for secret names and values

Quick Start

use wrkflw_secrets::prelude::*;

#[tokio::main]
async fn main() -> SecretResult<()> {
    let manager = SecretManager::default().await?;

    std::env::set_var("GITHUB_TOKEN", "ghp_your_token_here");
    let secret = manager.get_secret("GITHUB_TOKEN").await?;

    // Substitute in templates
    let mut sub = SecretSubstitution::new(&manager);
    let resolved = sub.substitute("Bearer ${{ secrets.GITHUB_TOKEN }}").await?;

    // Mask secrets in logs
    let mut masker = SecretMasker::new();
    masker.add_secret(secret.value());
    println!("{}", masker.mask(&resolved));

    Ok(())
}

Configuration

Create ~/.wrkflw/secrets.yml:

default_provider: env
enable_masking: true
timeout_seconds: 30
enable_caching: true
cache_ttl_seconds: 300

providers:
  env:
    type: environment
    prefix: "WRKFLW_SECRET_"
  file:
    type: file
    path: "~/.wrkflw/secrets.json"
  vault:
    type: vault
    url: "https://vault.example.com"
    auth:
      method: token
      token: "${VAULT_TOKEN}"
    mount_path: "secret"

Feature Flags

[dependencies]
wrkflw-secrets = { version = "0.7", features = ["vault-provider", "aws-provider"] }

Available: env-provider (default), file-provider (default), vault-provider, aws-provider, azure-provider, gcp-provider, all-providers.

See the secrets demo for end-to-end usage examples.