Skip to content

Commit 055b0fc

Browse files
feat(sdk): .tgignore file support (#633)
#### Motivation and context Set what files/folders should be ignored when using the custom `expand_path` function in an external `.tgignore` file. `.tgignore` will behave similarly to most .ignore files with basic glob syntax support. #### Migration notes `expand_glob` has been renamed to `expand_path` ### Checklist - [x] The change come with new or modified tests - [ ] Hard-to-understand functions have explanatory comments - [ ] End-user documentation is updated to reflect the change --------- Signed-off-by: Yohe-Am <[email protected]> Co-authored-by: Yohe-Am <[email protected]>
1 parent 630f506 commit 055b0fc

File tree

8 files changed

+142
-63
lines changed

8 files changed

+142
-63
lines changed

.github/workflows/tests.yml

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -175,59 +175,58 @@ jobs:
175175
cargo run --locked --package meta-cli -- --help
176176
cargo test --locked --package meta-cli
177177
178-
test-docker:
179-
needs: changes
180-
if: ${{ needs.changes.outputs.typegate == 'true' }}
181-
runs-on: ${{ matrix.runner }}
182-
strategy:
183-
fail-fast: false
184-
matrix:
185-
include:
186-
- platform: linux/amd64
187-
runner: ubuntu-latest
188-
# - platform: linux/amd64
189-
# runner: custom-ubuntu-large
190-
# FIXME: try macos-14 runner once all actions support it
191-
# docker buildx action broken as of 2024-02-09
192-
193-
# TODO
194-
# - platform: linux/arm64
195-
# runner: custom-macos
196-
steps:
197-
- uses: actions/checkout@v4
198-
- uses: docker/setup-buildx-action@v3
199-
- uses: scherermichael-oss/action-has-permission@master
200-
id: check
201-
with:
202-
required-permission: write
203-
env:
204-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
205-
- uses: docker/login-action@v3
206-
if: steps.check.outputs.has-permission
207-
with:
208-
registry: docker.io
209-
username: zifeo
210-
password: ${{ secrets.DOCKERHUB_TOKEN }}
211-
- name: Build with cache (internal)
212-
uses: docker/build-push-action@v5
213-
if: steps.check.outputs.has-permission
214-
with:
215-
file: dev/Dockerfile
216-
platforms: ${{ matrix.platform }}
217-
push: false
218-
cache-from: type=registry,ref=docker.io/zifeo/metatype-cache:ci
219-
cache-to: type=registry,ref=docker.io/zifeo/metatype-cache:ci,mode=max
220-
# check target runs extra validation steps in the Dockerfile
221-
target: check
222-
- name: Build without cache (external)
223-
uses: docker/build-push-action@v5
224-
if: "! steps.check.outputs.has-permission"
225-
with:
226-
file: dev/Dockerfile
227-
platforms: linux/amd64
228-
push: false
229-
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/typegate:latest
230-
target: check
178+
# test-docker:
179+
# needs: changes
180+
# if: ${{ needs.changes.outputs.typegate == 'true' }}
181+
# runs-on: ${{ matrix.runner }}
182+
# strategy:
183+
# fail-fast: false
184+
# matrix:
185+
# include:
186+
# - platform: linux/amd64
187+
# runner: ubuntu-latest
188+
# # - platform: linux/amd64
189+
# # runner: custom-ubuntu-large
190+
# # FIXME: try macos-14 runner once all actions support it
191+
# # docker buildx action broken as of 2024-02-09
192+
# # TODO
193+
# # - platform: linux/arm64
194+
# # runner: custom-macos
195+
# steps:
196+
# - uses: actions/checkout@v4
197+
# - uses: docker/setup-buildx-action@v3
198+
# - uses: scherermichael-oss/action-has-permission@master
199+
# id: check
200+
# with:
201+
# required-permission: write
202+
# env:
203+
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
204+
# - uses: docker/login-action@v3
205+
# if: steps.check.outputs.has-permission
206+
# with:
207+
# registry: docker.io
208+
# username: zifeo
209+
# password: ${{ secrets.DOCKERHUB_TOKEN }}
210+
# - name: Build with cache (internal)
211+
# uses: docker/build-push-action@v5
212+
# if: steps.check.outputs.has-permission
213+
# with:
214+
# file: dev/Dockerfile
215+
# platforms: ${{ matrix.platform }}
216+
# push: false
217+
# cache-from: type=registry,ref=docker.io/zifeo/metatype-cache:ci
218+
# cache-to: type=registry,ref=docker.io/zifeo/metatype-cache:ci,mode=max
219+
# # check target runs extra validation steps in the Dockerfile
220+
# target: check
221+
# - name: Build without cache (external)
222+
# uses: docker/build-push-action@v5
223+
# if: "! steps.check.outputs.has-permission"
224+
# with:
225+
# file: dev/Dockerfile
226+
# platforms: linux/amd64
227+
# push: false
228+
# cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/typegate:latest
229+
# target: check
231230

232231
test-full:
233232
needs: changes

examples/deploy/.tgignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.git
2+
node_modules
3+
4+
*.json
5+
*.y*ml
6+
.*ignore
7+
deploy.*

examples/deploy/deploy.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const artifactsConfig = {
7575
prismaMigration: {
7676
globalAction: {
7777
create: true,
78-
reset: false
78+
reset: true
7979
},
8080
migrationDir: path.join("prisma-migrations", tg.name)
8181
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.tgignore
2+
self_deploy_*.ts
3+
*.mjs

typegraph/core/src/utils/fs_host.rs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use indexmap::IndexMap;
1010

1111
use crate::{
1212
global_store::Store,
13-
wit::metatype::typegraph::host::{expand_glob, get_cwd, read_file, write_file},
13+
wit::metatype::typegraph::host::{
14+
expand_path as expand_path_host, get_cwd, path_exists, read_file, write_file,
15+
},
1416
};
1517

1618
pub fn read_text_file<P: Into<String>>(path: P) -> Result<String, String> {
@@ -26,7 +28,7 @@ pub fn write_text_file<P: Into<String>>(path: P, text: P) -> Result<(), String>
2628
}
2729

2830
pub fn common_prefix_paths(paths: &[PathBuf]) -> Option<PathBuf> {
29-
if paths.is_empty() {
31+
if paths.len() <= 1 {
3032
return None;
3133
}
3234

@@ -54,6 +56,15 @@ pub fn relativize_paths(paths: &[PathBuf]) -> Result<Vec<PathBuf>, String> {
5456
return Ok(vec![]);
5557
}
5658

59+
// ambiguous case, assume it is under cwd
60+
if paths.len() == 1 {
61+
let possible_base = cwd()?;
62+
return paths[0]
63+
.strip_prefix(&possible_base)
64+
.map(|stripped| vec![stripped.to_owned()])
65+
.map_err(|_| format!("{:?} does not contain path", possible_base.display()));
66+
}
67+
5768
if let Some(common_dir) = common_prefix_paths(paths) {
5869
let ret = paths
5970
.iter()
@@ -69,10 +80,44 @@ pub fn relativize_paths(paths: &[PathBuf]) -> Result<Vec<PathBuf>, String> {
6980
Err("Cannot relativize path list if one item is already relative".to_string())
7081
}
7182

83+
pub fn expand_path(path: &Path, exclude_glob: &[String]) -> Result<Vec<PathBuf>, String> {
84+
let exclude_as_regex = exclude_glob
85+
.iter()
86+
.map(|glob_pattern| {
87+
let mut regex_pattern = String::new();
88+
for c in glob_pattern.chars() {
89+
match c {
90+
'*' => regex_pattern.push_str(".*"),
91+
'?' => regex_pattern.push('.'),
92+
_ => {
93+
if ".()+-[]^$|".contains(c) {
94+
// escape native regex
95+
regex_pattern.push('\\');
96+
}
97+
regex_pattern.push(c);
98+
}
99+
}
100+
}
101+
// test as suffix if glob star is present
102+
if glob_pattern.contains('*') {
103+
regex_pattern.push('$');
104+
}
105+
regex_pattern
106+
})
107+
.collect::<Vec<_>>();
108+
109+
let ret = expand_path_host(&path.display().to_string(), &exclude_as_regex)?
110+
.iter()
111+
.map(PathBuf::from)
112+
.collect();
113+
114+
Ok(ret)
115+
}
116+
72117
pub fn compress<P: Into<String>>(path: P, exclude: Option<Vec<String>>) -> Result<Vec<u8>, String> {
73118
// Note: each exclude entry is a regex pattern
74119
let exclude = exclude.unwrap_or_default();
75-
let paths = expand_glob(&path.into(), &exclude)?;
120+
let paths = expand_path(&PathBuf::from(path.into()), &exclude)?;
76121
let mut entries = IndexMap::new();
77122
// eprint("Preparing tarball");
78123

@@ -103,11 +148,35 @@ pub fn unpack_base64<P: Into<String>>(tarb64: &str, dest: P) -> Result<(), Strin
103148
}
104149

105150
pub fn compress_and_encode_base64(path: PathBuf) -> Result<String, String> {
106-
let exclude = vec!["node_modules/".to_string(), "\\.git/".to_string()];
107-
let bytes = compress(path.display().to_string(), Some(exclude))?;
151+
let mut tgignore = load_tg_ignore_file()?;
152+
let default = vec!["node_modules".to_string(), ".git".to_string()];
153+
tgignore.extend(default);
154+
155+
let bytes = compress(path.display().to_string(), Some(tgignore))?;
108156
encode_bytes_to_base_64(bytes).map_err(|e| e.to_string())
109157
}
110158

159+
/// Search for .tgignore file at `cwd`, if nothing is found, an empty `Vec` is returned
160+
pub fn load_tg_ignore_file() -> Result<Vec<String>, String> {
161+
let file = cwd()?.join(".tgignore").display().to_string();
162+
163+
match path_exists(&file)? {
164+
true => read_text_file(file).map(|content| {
165+
content
166+
.lines()
167+
.filter_map(|line| {
168+
let trimmed = line.trim();
169+
if trimmed.is_empty() || trimmed.starts_with('#') {
170+
return None;
171+
}
172+
Some(line.to_owned())
173+
})
174+
.collect()
175+
}),
176+
false => Ok(vec![]),
177+
}
178+
}
179+
111180
/// Returns `get_cwd()` by default, custom `dir` otherwise
112181
pub fn cwd() -> Result<PathBuf, String> {
113182
match Store::get_deploy_cwd() {
@@ -116,12 +185,14 @@ pub fn cwd() -> Result<PathBuf, String> {
116185
}
117186
}
118187

188+
/// Strip given path with `cwd`
119189
pub fn make_relative(path: &Path) -> Result<PathBuf, String> {
120190
path.strip_prefix(cwd()?)
121191
.map_err(|e| e.to_string())
122192
.map(|r| r.to_owned())
123193
}
124194

195+
/// Join given path with `cwd`
125196
pub fn make_absolute(path: &Path) -> Result<PathBuf, String> {
126197
match path.is_relative() {
127198
true => Ok(cwd()?.join(path)),

typegraph/core/wit/typegraph.wit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ interface utils {
557557
interface host {
558558
print: func(s: string)
559559
eprint: func(s: string)
560-
expand-glob: func(root: string, exclude: list<string>) -> result<list<string>, string>
560+
expand-path: func(root: string, exclude: list<string>) -> result<list<string>, string>
561561
path-exists: func(path: string) -> result<bool, string>
562562
read-file: func(path: string) -> result<list<u8>, string>
563563
write-file: func(path: string, data: list<u8>) -> result<_, string>

typegraph/node/sdk/src/host/host.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,12 @@ function listAllFilesHelper(
2626
}
2727
}
2828

29-
export function expandGlob(
29+
export function expandPath(
3030
root: string,
3131
exclude: Array<string>,
3232
): Array<string> {
3333
try {
3434
const ret = [] as Array<string>;
35-
// console.log("[host] received args", root, exclude);
3635
listAllFilesHelper(root, ret, exclude);
3736
return ret;
3837
} catch (err) {

typegraph/python/typegraph/host/host.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def print(self, msg: str):
2424
def eprint(self, msg: str):
2525
print(msg, file=sys.stderr)
2626

27-
def expand_glob(self, root: str, exclude: List[str]) -> Result[List[str], str]:
27+
def expand_path(self, root: str, exclude: List[str]) -> Result[List[str], str]:
2828
try:
2929
result = []
3030
for path, _, files in os.walk(root):

0 commit comments

Comments
 (0)