Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge queue: embarking main (fa4a7a5) and #6235 together #6301

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c0d1315
feat(cli): wing secrets
hasanaburayyan Apr 8, 2024
40bfd06
tidy
hasanaburayyan Apr 14, 2024
ed37da4
updates
hasanaburayyan Apr 14, 2024
df06916
testing
hasanaburayyan Apr 16, 2024
f8a83b2
wip
hasanaburayyan Apr 17, 2024
744ff53
tf-aws working
hasanaburayyan Apr 17, 2024
80fc610
fix list command
hasanaburayyan Apr 17, 2024
b157875
test
hasanaburayyan Apr 17, 2024
40a3404
clean up
hasanaburayyan Apr 17, 2024
d779b78
wip
hasanaburayyan Apr 17, 2024
7c8b4a2
somthings cooking
hasanaburayyan Apr 17, 2024
7bcb0ca
i think its gonna fix it
hasanaburayyan Apr 17, 2024
a31bd2e
tidy
hasanaburayyan Apr 17, 2024
5e0a356
oops
hasanaburayyan Apr 17, 2024
b444b62
fixed tests
hasanaburayyan Apr 17, 2024
82c3622
shadowing issue
hasanaburayyan Apr 17, 2024
9583787
fix
hasanaburayyan Apr 17, 2024
6bbed5a
fuck circular dependencies
hasanaburayyan Apr 17, 2024
75b2ba3
Merge branch 'main' into hasan/create-secrets
monadabot Apr 17, 2024
88c09ca
chore: self mutation (build.diff)
monadabot Apr 17, 2024
5d82861
docs
hasanaburayyan Apr 17, 2024
961847f
tidy
hasanaburayyan Apr 17, 2024
774c3a7
fix compatibility spy
hasanaburayyan Apr 17, 2024
e537802
Merge branch 'main' into hasan/create-secrets
monadabot Apr 17, 2024
605910d
chore: self mutation (build.diff)
monadabot Apr 17, 2024
ef611b9
adding const
hasanaburayyan Apr 18, 2024
b039def
add type
hasanaburayyan Apr 19, 2024
95c4456
updates from pr review
hasanaburayyan Apr 22, 2024
8c65fcc
fix
hasanaburayyan Apr 22, 2024
b6da749
fix source dir issue
hasanaburayyan Apr 22, 2024
50467f5
Merge branch 'main' into hasan/create-secrets
monadabot Apr 22, 2024
8a66197
chore: self mutation (build.diff)
monadabot Apr 22, 2024
1c46928
Merge of #6235
mergify[bot] Apr 23, 2024
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
17 changes: 17 additions & 0 deletions apps/wing/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,23 @@ async function main() {
.hook("preAction", collectAnalyticsHook)
.action(runSubCommand("compile"));

program
.command("secrets")
.description("Manage secrets")
.argument("[entrypoint]", "program .w entrypoint")
.option(
"-t, --platform <platform> --platform <platform>",
"Target platform provider (builtin: sim, tf-aws, tf-azure, tf-gcp, awscdk)",
collectPlatformVariadic,
DEFAULT_PLATFORM
)
.option("-v, --value <value>", "Platform-specific value in the form KEY=VALUE", addValue, [])
.option("--values <file>", "File with platform-specific values (TOML|YAML|JSON)")
.addOption(new Option("--list", "List required application secrets"))
.hook("preAction", progressHook)
.hook("preAction", collectAnalyticsHook)
.action(runSubCommand("secrets"));

program
.command("test")
.description(
Expand Down
61 changes: 61 additions & 0 deletions apps/wing/src/commands/secrets.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { writeFile } from "fs/promises";
import { join } from "path";
import { BuiltinPlatform } from "@winglang/compiler";
import { describe, expect, test, vitest, beforeEach, afterEach, vi } from "vitest";
import { secrets } from "../commands/secrets";
import { generateTmpDir } from "../util";

vitest.mock("inquirer");

describe("secrets", () => {
let log: any;

beforeEach(() => {
log = console.log;
console.log = vi.fn();
});

afterEach(() => {
console.log = log;
});

test("no secrets found", async () => {
const workdir = await generateTmpDir();
process.chdir(workdir);

const wingCode = `
bring cloud;
`;

await writeFile(join(workdir, "main.w"), wingCode);

await secrets("main.w", {
platform: [BuiltinPlatform.SIM],
targetDir: workdir,
});

expect(console.log).toHaveBeenCalledWith("0 secret(s) found\n");
});

test("secrets found", async () => {
const workdir = await generateTmpDir();
process.chdir(workdir);

const wingCode = `
bring cloud;
let s1 = new cloud.Secret(name: "my-secret") as "s1";
let s2 = new cloud.Secret(name: "other-secret") as "s2";
`;

await writeFile(join(workdir, "main.w"), wingCode);

await secrets("main.w", {
platform: [BuiltinPlatform.SIM],
targetDir: workdir,
list: true,
});

expect(console.log).toHaveBeenCalledWith("2 secret(s) found\n");
});
});
47 changes: 47 additions & 0 deletions apps/wing/src/commands/secrets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import fs from "fs";
import path from "path";
import { PlatformManager, SECRETS_FILE_NAME } from "@winglang/sdk/lib/platform";
import inquirer from "inquirer";
import { CompileOptions, compile } from "./compile";

export interface SecretsOptions extends CompileOptions {
readonly list?: boolean;
}

export async function secrets(entrypoint?: string, options?: SecretsOptions): Promise<void> {
// Compile the program to generate secrets file
const outdir = await compile(entrypoint, options);
const secretsFile = path.join(outdir, SECRETS_FILE_NAME);

let secretNames = fs.existsSync(secretsFile)
? JSON.parse(fs.readFileSync(secretsFile, "utf-8"))
: [];

process.env.WING_SOURCE_DIR = path.resolve(path.dirname(entrypoint ?? "main.w"));

let secretValues: Record<string, string> = {};
console.log(`${secretNames.length} secret(s) found\n`);

if (options?.list) {
console.log("- " + secretNames.join("\n- "));
return;
}

if (secretNames.length === 0) {
return;
}

for (const secret of secretNames) {
const response = await inquirer.prompt([
{
type: "password",
name: "value",
message: `Enter the secret value for ${secret}:`,
},
]);
secretValues[secret] = response.value;
}

const plaformManager = new PlatformManager({ platformPaths: options?.platform });
await plaformManager.storeSecrets(secretValues);
}
24 changes: 18 additions & 6 deletions docs/docs/04-standard-library/cloud/secret.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@ new cloud.Function(inflight () => {

### Simulator (`sim`)

When using a secret in Wing's simulator, a secrets file must be added to your home directory at `~/.wing/secrets.json`.
When using a secret in Wing's simulator, a secrets file must be added to your project in a file called: `.env`.
The simulator will look up secrets in this file by their `name`.
Secrets should be saved in a JSON format:
Secrets should be saved in a key=value format:

```json
// secrets.json
{
"my-api-key": "1234567890"
}
// .env
my-api-key=1234567890
secret-key=secret-value
```

### AWS (`tf-aws` and `awscdk`)
Expand Down Expand Up @@ -189,6 +188,7 @@ other capabilities to the inflight host.
| **Name** | **Type** | **Description** |
| --- | --- | --- |
| <code><a href="#@winglang/sdk.cloud.Secret.property.node">node</a></code> | <code>constructs.Node</code> | The tree node. |
| <code><a href="#@winglang/sdk.cloud.Secret.property.name">name</a></code> | <code>str</code> | Get secret name. |

---

Expand All @@ -204,6 +204,18 @@ The tree node.

---

##### `name`<sup>Optional</sup> <a name="name" id="@winglang/sdk.cloud.Secret.property.name"></a>

```wing
name: str;
```

- *Type:* str

Get secret name.

---



## Structs <a name="Structs" id="Structs"></a>
Expand Down
25 changes: 25 additions & 0 deletions docs/docs/06-tools/01-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,31 @@ This will compile your current Wing directory, and bundle it as a tarball that c
See [Libraries](../05-libraries.md) for more details on packaging and consuming Wing libraries.
:::
## Store Secrets: `wing secrets`
The `wing secrets` command can be used to store secrets needed by your application. The method of storing secrets depends on the target platform.
Take the following Wing application:
```js
// main.w
bring cloud;
let secret = new cloud.Secret(name: "slack-token");
```
Usage:
```sh
$ wing secrets main.w
1 secret(s) found
? Enter the secret value for slack-token: [hidden]
```
## Environment Variables
For development and testing, Wing can automatically read environment variables from `.env` files in your current working directory. These environment variables can be accessed in Wing code using `util.env` and `util.tryEnv` in both preflight. In inflight these functions can also be used but note that the variables are not automatically available, if desired they must be passed explicitly when used like in `cloud.Function`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 7
documentation:
kind: markdown
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n- `name?` — `str?` — Get secret name.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
sortText: gg|Secret
- label: Service
kind: 7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 7
documentation:
kind: markdown
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n- `name?` — `str?` — Get secret name.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
sortText: gg|Secret
- label: Service
kind: 7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 7
documentation:
kind: markdown
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n- `name?` — `str?` — Get secret name.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
sortText: gg|Secret
insertText: Secret($1)
insertTextFormat: 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 7
documentation:
kind: markdown
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n- `name?` — `str?` — Get secret name.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
sortText: gg|Secret
- label: Service
kind: 7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 7
documentation:
kind: markdown
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
value: "```wing\nclass Secret\n```\n---\nA cloud secret.\n\n### Initializer\n- `...props` — `SecretProps?`\n \n - `name?` — `str?` — The secret's name.\n### Fields\n- `node` — `Node` — The tree node.\n- `name?` — `str?` — Get secret name.\n### Methods\n- `isConstruct` — `preflight (x: any): bool` — Checks if `x` is a construct.\n- `onLift` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this resource inflight.\n- `onLiftType` — `preflight (host: IInflightHost, ops: Array<str>): void` — A hook called by the Wing compiler once for each inflight host that needs to use this type inflight.\n- `toString` — `preflight (): str` — Returns a string representation of this construct.\n- `value` — `inflight (options: GetSecretValueOptions?): str` — Retrieve the value of the secret.\n- `valueJson` — `inflight (options: GetSecretValueOptions?): Json` — Retrieve the Json value of the secret."
sortText: gg|Secret
- label: Service
kind: 7
Expand Down
11 changes: 5 additions & 6 deletions libs/wingsdk/src/cloud/secret.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@ new cloud.Function(inflight () => {

### Simulator (`sim`)

When using a secret in Wing's simulator, a secrets file must be added to your home directory at `~/.wing/secrets.json`.
When using a secret in Wing's simulator, a secrets file must be added to your project in a file called: `.env`.
The simulator will look up secrets in this file by their `name`.
Secrets should be saved in a JSON format:
Secrets should be saved in a key=value format:

```json
// secrets.json
{
"my-api-key": "1234567890"
}
// .env
my-api-key=1234567890
secret-key=secret-value
```

### AWS (`tf-aws` and `awscdk`)
Expand Down
14 changes: 12 additions & 2 deletions libs/wingsdk/src/cloud/secret.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Construct } from "constructs";
import { fqnForType } from "../constants";
import { INFLIGHT_SYMBOL } from "../core/types";
import { INFLIGHT_SYMBOL, SECRET_SYMBOL } from "../core/types";
import { Json, Node, Resource } from "../std";

/**
Expand Down Expand Up @@ -33,6 +33,11 @@ export interface SecretProps {
export class Secret extends Resource {
/** @internal */
public [INFLIGHT_SYMBOL]?: ISecretClient;
/** @internal */
public [SECRET_SYMBOL] = true;

/** @internal */
protected _name?: string;

constructor(scope: Construct, id: string, props: SecretProps = {}) {
if (new.target === Secret) {
Expand All @@ -44,7 +49,12 @@ export class Secret extends Resource {
Node.of(this).title = "Secret";
Node.of(this).description = "A cloud secret";

props;
this._name = props.name;
}

/** Get secret name */
public get name(): string | undefined {
return this._name;
}
}

Expand Down
6 changes: 6 additions & 0 deletions libs/wingsdk/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export { Construct } from "constructs";
/** Flag to signify the `inflight` side of a `preflight` object */
export const INFLIGHT_SYMBOL: unique symbol = Symbol("@winglang/sdk.inflight");

/** This symbol is not defined in cloud/secrets.ts due to circular dependencies
* between cloud/secrets.ts and platform/platform-manager.ts, which need to be revisited
* in the meantime this dependency inversion is used to avoid the circular dependency
*/
export const SECRET_SYMBOL = Symbol("@winglang/sdk.cloud.Secret");

/** `preflight` representation of an `inflight` */
export type Inflight<F extends AsyncFunction> = IInflight & {
/** Note: This is not actually callable,
Expand Down
Loading
Loading