How do I make vendoring work with Google App Engine? - google-app-engine

I am trying to introduce Go vendoring (storing dependencies in a folder called vendor) to an existing App Engine project. I have stored all dependencies in the vendor folder (using Godep as a helper) and it looks right, but running the application locally I get the following error:
go-app-builder: Failed parsing input: package "golang.org/x/net/context" is imported from multiple locations: "/Users/erik/go/src/github.com/xyz/abc/vendor/golang.org/x/net/context" and "/Users/erik/go/src/golang.org/x/net/context"
I believe the two locations should resolve to the same location, as Go applications should look in the vendor folder first. Is there a way to make Appengine understand that both dependencies are the same?

Your project directory (where app.yaml is) is probably in the GOPATH/src.
It shouldn't be.
The go-app-builder will take everything in the app.yaml folder (and below) and additionally merge your GOPATH into it, meaning now you have it twice.
The solution is to move app.yaml out of the GOPATH/src folder.
Additionally you'll find that goapp test works differently from goapp serve and goapp deploy when it comes to resolving dependencies.
So this is the solution I have been using (haven't used golang app engine in a while already) and it's the only setup I've found to work properly for all the goapp commands and for govendor to work properly (not sure about godep)
/GOPATH
├──/appengine
| ├── app.yaml
| └── aeloader.go
└──/src
└── /MYPROJECT
├── main.go
├── /handler
| └── handler.go
└── /vendor
details:
file: GOPATH/appengine/aeloader.go (NOTE the init function is necessary, probably a bug though)
package mypackage
import (
_ "MYPROJECT"
)
func init() {
}
now run goapp serve and goapp deploy from ../GOPATH/appengine/ and goapp test ./... from ../GOPATH/src/MYPROJECT
P.S. I find the global GOPATH thing silly and simply set my GOPATH to current project folder (in the example above /GOPATH) and check the whole thing into version control.

I use a Makefile to move the vendor directory to a temporary GOPATH:
TMPGOPATH := $(shell mktemp -d)
deploy:
mv vendor $(TMPGOPATH)/src
GOPATH=$(TMPGOPATH) gcloud app deploy
mv $(TMPGOPATH)/src vendor
I store this Makefile at the root of my service near the vendor directory and simply use make deploy to deploy manually or from the CI.
It works with Glide, Godeps or any tool that respects the Go vendor spec.
Please note, that you really need to move the vendor directory out of the build directory, otherwise the GoAppEngine compiler will try to build the vendor dependencies, potentially causing compile errors.

I just ran into this issue myself actually. The problem occurs when you're using the App Engine tools to build any package which imports something that is using vendoring, but the package you're trying to run doesn't have the import within it's vendor directory.
So, for example, if I'm trying to run package foo, which imports package bar, and both of which use the github.com/gorilla/mux library, if the bar repository has a vendor/ directory that contains gorilla/mux, but the foo package doesn't have gorilla mux in it's vendor/ directory, this error will occur.
The reason this happens is that the bar package will prioritize it's own vendor package over the one in the GOPATH, which is what foo will be using, causing a difference in the actual location of the imported paths.
The solution I found to this issue is to make sure that the foo directory is in the GOPATH and has the vendor directory properly installed. It's important to note that the vendor/ convention only works from within the GOPATH.

I managed to resolve this error using govendor instead of Godeps. The root cause appears to have been that vendored references with their own vendored references was not resolved correctly by Godeps.
The answer provided by Su-Au Hwang is also correct - you do have to keep app.yaml separate from your source.

Also got the same problem.
In the docs Google suggests the following:
For best results, we recommend the following:
Create a separate directory in your app's directory for each service.
Each service's directory should contain the service's app.yaml file and one or more .go files.
Do not include any subdirectories in a service's directory.
Your GOPATH should specify a directory that is outside your app's directory and contain all the dependencies that your app imports.
But this messes up my project structure, which looks like this:
GOPATH/
└── src
└── github.com
└── username
└── myproject
├── app.yaml
├── cmd
│   └── myproject
│   └── main.go
├── handlers
│   └── api.go
├── mw
│   ├── auth.go
│   └── logger.go
└── vendor
Where the myproject directory is a git project and the vendor folder contains all dependencies.
Running gcloud deploy from the myproject directory where app.yaml file lives doesn't work because first, main.go file is not in the same directory and second (from the same doc):
you must be careful not to place your source code at or below your app's directory where the app.yaml file is located
What I ended up doing is building my own custom runtime instead, which turned out to be a very clean solution.
Simply generate the Dockerfile with the following command:
gcloud beta app gen-config --custom
Modify it, then specify runtime: custom in your app.yaml and deploy normally.
The trick here is of course that you're in control what gets copied where.
Here is my Dockerfile:
# Dockerfile extending the generic Go image with application files for a
# single application.
FROM gcr.io/google-appengine/golang
ENV GOPATH /go
# The files which are copied are specified in the .dockerignore file
COPY . /go/src/github.com/username/myproject/
WORKDIR /go/src/github.com/username/myproject/
RUN go build -o dist/bin/myproject ./cmd/myproject
# All configuration parameters are passed through environment variables and specified in app.yaml
CMD ["/go/src/github.com/username/myproject/dist/bin/myproject"]
Don't forget that App Engine expects your application listening on port 8080. Check out Building Custom Runtimes doc for more details.

Related

Can't import files defined as aliases in tsconfig.json under paths

I am trying to share enums and dtos between my backend and frontend (NestJS at the back, React at the front. Both use typescript).
To do so, I tried to add my "shared" files to the client's tsconfig.json > compilerOptions > paths like so:
"paths": {
"#project/shared/*": [
"../server/src/common/*"
]
}
Unfortunately, this doesn't seem to work. When I run the react client, it throws "webpack Module not found."
This is my file structure:
App.tsx consumes the TestEnum from the server's common directory.
What you are trying to achieve is essentially a monorepo. The best way to solve the dependency issue is of monorepo is to refer each package as a separate package; it's clear in the comment that you do not want to do so but it's de facto standard already.
It's because importing what's outside the workspace(where the package.json lies) is really really bad idea. I've tried this but not recommend it; the package managers, bundlers and IDEs(let's say "the tools") will work strangely due to conflict issues:
the file's root has it's own config such as tsconfig.json, package.json which can conflict with project's setting. it is not clear which one they should follow.
the tools usually have their boundary due to security issues; if the tool can leave the project root, it can do something sneaky, such as compromising internal system files.
when deploying the apps in the clouds, they(client/server) have to stick together all the time. it becomes devOps issue.
Solution
the best way to deal with this, is to use workspace feature of package managers. here the term workspace means separate packages inside monorepo. npm's explanation of workspace clearly fits to your settings.
Workspaces is a generic term that refers to the set of features in the npm cli that provides support to managing multiple packages from your local file system from within a singular top-level, root package.
npm and yarn both support workspace features.
npm workspace
yarn workspace
from now on, the project structure should be perceived as this.
project root --> place workspace setting here
|-package root(client) --> workspace
|-package root(server) -> workspace too
most workspace features support 'hoist' feature. without publishing the actual packages, your packages in different folders can be imported and referred as if they are published, via softlink(symlink, something like shortcut in windows). it's same as typescript config's path alias but it actually creates a real file so that the tool won't have any side effects.
to do so, setup workspace config at your project root so that the tool can perceive this packages as workspaces.
# create shared package directory first
$(projectRoot) mkdir shared
// projectRoot/package.json
{
"name": "project",
"workspaces": [
"client",
"server",
"shared"
]
}
// projectRoot/client/package.json
{
"name": "#project/client",
"private": true, // set this to private so that this won't get published by mistake
"packages": {
// your default packages
"#project/shared": "1.0.0" // <-- version has to match
}
// ...
}
// projectRoot/server/package.json
{
"name": "#project/server",
"private": true
// ...
}
// projectRoot/shared/package.json
{
"name": "#project/shared",
"private": false, // make it public so that you can publish it later
"version": "1.0.0"
// ...
}
# remove all `node_modules` folder.
$(projectRoot) rm -rf client/node_modules
$(projectRoot) rm -rf server/node_modules
# reinstall node_modules so that the projects can be symlinked as packages.
$(projectRoot) npm i
then it'll work as workspace from now on and the packages will be linked. it's that simple!
Caveat
as you can see, cleaning up and bootstrapping the package is bit of a work. that's where monorepo tool(i.g. lerna) comes handy; try expand your monorepo knowledge from here.
if it's deployed to AWS, it'll probably work. however, it's best to publish the #project/shared package to npm in the end. many cloud instances don't support symlinks(vercel, netlify...) still, symlink is useful in local/dev environment.
Edit
here's working demo:https://github.com/rabelais88/monorepo-workspace-npm
check readme before proceed. after typing npm install, symlinked hollow modules are seen. as far as they are there, scripts will run fine
edit 2: after setting up the workspace, some dependencies or all the dependencies will be placed at project root instead of each workspace. it is intentional by design, to avoid having duplicated packages in each workspace. this might surprise some but actually very useful in maintaining bigger systems.
I want to share what I had to do in my projects and file structure to share models, types, enums, entities, etc., between the server (NestJS) and the client (React).
First of all, I started setting up npm workspaces to achieve a monorepo, as #sungryeol described in his detailed answer (thanks for it).
Unfortunately, it was only half of the way to making it work. So here is a solution I came up with (remarks are welcome) in addition to workspaces:
Client (react)
create react app is designed to compile code only from the src directory. Because I am using typescript in my shared code, I need to compile it to make it work. To make CRA compile my shared code, I used react-app-rewired to modify the webpack config with the following config-overrides.js file:
const path = require('path');
module.exports = function override(config, env) {
const babelLoaderModule = require.resolve('babel-loader');
const rule = config.module.rules[1].oneOf.find(rule => rule.loader === babelLoaderModule);
rule.include = [
rule.include,
path.resolve(__dirname, '../shared')
];
return config;
}
React finally started accepting my shared code without any issues.
Server (NestJS)
Since NestJS uses tsc as the compiler, I had to specify the directory of the shared code in the server's tsconfig.json as a path.
"paths": {
"#managed-businesses/shared/*": [
"../shared/*"
]
}
This made my shared code compile when I started the nest app, but it couldn't find the entry point, as it compiled the code in the following structure
dist
├── server
│ ├── src
│ ├── main.d.ts
│ └── main.js
├── shared
│ ├── dtos
│ └── enums
└── tsconfig.build.tsBuildInfo
while Nest lookup for the src/main.js file to be at the root of the dist directory.
To fix that, I had to tweak the nest-cli.json file to lookup the file in the dist/server/src directory. Here is my new config for nest-cli.
{
"collection": "#nestjs/schematics",
"sourceRoot": "server/src"
}
After all, the server finally works with the shared code, and I don't need to duplicate it between the projects. I am probably missing something or did something incorrectly, and I'll be happy to know if there is a better way, but at the bottom line, it works.

Having git problem in React django project

STATEMENT
I want to make project with django backend and React frontend. I have created a project using and then create a frontend react folder using create-react-app. Now I want to upload my projectname folder to my github repository. But when I add my files using git add . command from my root folder('projectname' folder). It shows some warnings given below. What should I do? Please help.
WARNING
hint: You've added another git repository inside your current repository.
hint: Clones of the outer repository will not contain the contents of
hint: the embedded repository and will not know how to obtain it.
hint: If you meant to add a submodule, use:
hint:
hint: git submodule add <url> frontend
hint:
hint: If you added this path by mistake, you can remove it from the
hint: index with:
hint:
hint: git rm --cached frontend
COMMAND THAT I HAVE USED
$ virtualenv env
$ source env/bin/activate
$ pip install django
$ django-admin.py startproject projectname
$ cd django-react-demo
$ npm install -g create-react-app
$ create-react-app frontend
MY FOLDER STRUCTURE
projectname
│
└───frontend
│ ├──.node_modules
│ ├──public
│ ├──src
│ ├──gitignore
│ ├──README.md
│ ├──package.json
│ └──package_lock.json
│
│projectname
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
You are seeing that error because you have a git repository nested inside another repository.
Your main project directory projectname has a .git directory, and the directory nested inside it frontend has another .git repo that create-react-app created. The git repo inside another repo is called a submodule. It's possible to work with submodules, but it has its own quirks.
The easiest way to get around your error is to use only one git repo in your main project directory and delete the .git directory inside frontend directory. Try the following steps:
Go into the frontend directory.
cd frontend
Delete the .git directory inside frontend directory.
rm -rf .git
Go back to your main project directory.
You should now be able to track all files inside the frontend directory inside your main directory.
Maybe your react project i.e. frontend is also a git repository. So, what you can do is, put the frontend on the outside of the projectname folder and use the API key that you have from the backend in the frontend for your work.
You can follow this link:
https://www.digitalocean.com/community/tutorials/build-a-to-do-application-using-django-and-react

How to compile TypeScript/Less files into JavaScript/CSS by using webpack and Babel?

Currently, I am developing a package and going to publish it in npm, I wrote it with TypeScript but faced some problems in package bundling.
I put my codes (TypeScript/Less files) in src and the structure shows below:
src
├── components
│   ├── table.less
│   └── table.tsx
├── index.tsx
└── utils
└── mock.tsx
Since I want to publish it in npm, so I need it be compiled to JavaScript/CSS files (in lib folder) so that other developers can import it directly (without extra compiling in their project), the structure should be like this:
lib
├── components
│   ├── table.css
│   └── table.js
├── index.js
└── utils
└── mock.js
But I faced some problems:
If I use tsc command, tsx files can be compiled to js files rightly, but less files will be ignored;
If I use webpack commands rather that tsc, the result will be bundled in one file, and lost it's original structure, and it will confuse package users;
I think I need to make it works by:
Compile all files from src to lib one by one(Keep the same folder structure);
tsx files to js files;
less files to css files;
add declaration files such as index.js.d.ts and index.css.d.ts;
modify some writing styles such as import styles from './index.less' to import styles from './index.css'; Or inject stylesheets into js files directly; (I am not sure about this step)
Bundle one js file with all of things in it (with webpack), as well as minimized version;
The package contains JSX grammar since I used React in it.
As I know, I need to use Babel in compiling TS/JS codes, and webpack in compiling less and other assets, but I am confused about how to combine them in working together.
So any suggestions on how to combine cool tools in solving my problem? I looked through really a lot tutorials but most of them are React/Less/TypeScript Project (not package development) or TypeScript package (without using less/css).
Thanks really a lot.
This question has been around for a long time, I hope my answer can still help people who click in to find the answer:
As you said, .tsx files can be compiled by tsc into .ts files, but .scss or .less not. so you need to use node-sass or less to process them.
Based on your directory structure above, you can write the following commands under scripts in package.json:
if you use less:
"scripts": {
"build:css": "lessc src/components/table.less lib/components/table.css"
}
or node-sass:
"scripts": {
"build:css": "node-sass -r src -o lib"
}
Yes, node-sass scans the root folder and automatically compiles the .scss files in it into .css files in the corresponding directory. Using less may require you to spend more time exploring the useful methods.
But just converting to css files doesn't help, because the style files introduced in the js files still have .less or .scss suffixes, we need to replace this part of the .js file, you may think of using node to read each .js file, and then use the regular match, this idea is generally no problem, but the global matching of the regular replacement can cause some hidden problems (although almost impossible), so I use AST to do the replacement.
Yes, I wrote an easy-to-use command line tool for this, you just need to:
npm i tsccss -D
and add npm script:
"scripts": {
"build:css": "node-sass -r src -o lib",
"compile": "tsc -p tsconfig.json && tsccss -o lib",
"build": "npm run build:css && npm run compile"
}
Yes, as you can see, tsccss -o lib is completely enough.
Hope you enjoy it, here is github/repo: tsccss

Rake to build a C application

I'm attempting to migrate a C application I have been working on to use Rake insead of GNU Make. The file tree is something like:
project
├── LICENSE.md
├── Makefile
├── Rakefile
├── README.md
└── src
├── debug.h
├── main.c
├── queue.c
├── queue.h
└── ui
├── ui.c
└── ui.h
I want to build each file in a separate build directory and generate the dependencies of each .c file with either gcc or clang in a deps directory.
I cannot seem to find any examples of how to write a Rakefile to compile a C project. Does anyone have a link or some advice to help me get started?
EDIT: I have a temporary Rakefile that accomplishes some of the tasks I want it to eventually do. I would like to detect if clang is installed and use clang or gcc.
Here is my current Rakefile https://github.com/Rostepher/spoticli/blob/master/Rakefile
Use Ceedling!
https://github.com/ThrowTheSwitch/Ceedling
It's an automated unit test framework for C, but does a great job of simply building as well. You should be able to get what your looking for by editing a YAML config file, and you'll get Rake tasks for building out-of-the box. Of course you can create your own custom Rake tasks as well.
As a bonus you can create mocks with CMock and automated unit tests with Unity.
It's super easy to install from a gem:
> gem install ceedling
If you're trying to generate a native extension so you can compile a Ruby interface to C code this is a different issue, but I think what you are looking for is how to use mkmf from rake. While you could bare-knuckle a rakefile into acting like make for a C project it would be a lot of extra effort that tools like mkmf have already done for you. Take a look at:
http://ruby-doc.org/stdlib-2.0.0/libdoc/mkmf/rdoc/MakeMakefile.html
Basically you'll just pass the arguments needed to mkmf and it will generate a makefile for you and run it. The documentation here says it's for building extensions but if you don't write any extension code that's irrelevant and it will just compile your code like a raw makefile would.
As a side note: If you're looking for a more dynamic make tool or a tool to dynamically generate makefiles CMake would be a better option than putting together a hackish rake build.

How do I create a new Google App Engine project in IntelliJ 12 which works?

I'll begin by saying that I have no prior GAE experience - I'm trying to get GAE working in IntelliJ 12 but having issues, was wondering if anyone could have a look over what I'm doing and tell me if there's anything wonky here.
Steps:
Create Java project in IntelliJ with JDK 1.7.0_51. Click Next.
Select Web Application > Google App Engine on desired techs page
with path to appengine-java-sdk-1.8.9. Click Finish.
Copy files from appengine-java-sdk-1.8.9/demos/new_project_template/ to project
directory
I now have a main directory structure like:
.
├── COPYING
├── build.xml
├── html
│   └── index.html
├── src
│   ├── META-INF
│   │   └── jdoconfig.xml
│   ├── WEB-INF
│   │   ├── appengine-web.xml
│   │   └── web.xml
│   ├── log4j.properties
│   ├── logging.properties
│   └── org
│   └── example
│   └── HelloAppEngineServlet.java
├── test.iml
└── web
├── WEB-INF
│   ├── appengine-web.xml
│   └── web.xml
└── index.jsp
Running this will run the webserver with the index.jsp in the web directory.
A few questions around this - should there be a 'web' and an 'html' directory? Why are there two WEB-INF directories and should they both be the same? Should I manually edit both of them each time I update one?
If I then follow the instructions at https://developers.google.com/appengine/docs/java/gettingstarted/creating it mentions a 'war' folder - I confess that I'm confused about the 'war', 'web' and 'html' folders - I think somewhere I've also seen referenced a 'www' folder. Do these folder names actually matter?
Following the tutorial I create a guestbook folder within the 'src' folder and make the java file. When I enter the info in the web.xml (both of them) I get an error for the line
<servlet-name>guestbook</servlet-name>
"A field of identity constraint 'web-app-servlet-name-uniqueness' matched element 'web-app', but this element does not have a simple type"
To top that off - guestbook.GuestbookServlet doesn't resolve.
There has to be a simpler way of getting this running in Intellij - can anyone help me?
Unfortunately, IntelliJ does not make this process simple. It seems like they expect you to use Maven to handle a lot of this. But this makes things a lot harder on people trying to get started with GAE on IntelliJ.
Your project is a mess right now. You have combined things that IntelliJ added for your web module with some of the files from the demo projects. So to start, remove your files and remove your web module from IntelliJ.
Now go back to the demo folder that you want to use, it should include the COPYING, build.xml, and a src and war dir. Copy all of those to your project. Then go into project structure->modules and import module. This will allow IntelliJ to detect your web module and avoid creating duplicate files and dirs.
You also need to configure your Application Server under Settings->IDE Settings->Application Servers. Add a Google App Engine Dev Server and specify your SDK directory.
Then go back to your Project Structure->Module->Dependencies and add a Library. Select the Application Server Library that you just defined. If your project uses more advanced features of GAE, you will need to go to Project Structure->Artifacts and add the libraries to your artifact.
Also for the settings on the Artifact, you need to create an 'exploded war' definition that points to your war dir.
There is likely more configuration needed... but I can't think of it all right now. Let me know what you get stuck on next and I can try to help.
IntelliJ IDEA 14 Ultimate has integrated GAE support. How comprehensive this is I'm not totally sure yet. I'll update this answer shortly with more details.
https://www.jetbrains.com/idea/features/google_app_engine.html

Resources